当前位置: 首页 > news >正文

【python进阶攻略13】协程、内存copy、多进程

协程

Python中的协程和生成器很相似但又稍有不同。主要区别在于:

  • 生成器是数据的生产者
  • 协程则是数据的消费者

首先我们先来回顾下生成器的创建过程。我们可以这样去创建一个生成器:

    def fib():a, b = 0, 1while True:yield aa, b = b, a+b

然后我们经常在for循环中这样使用它:

    for i in fib():print i

这样做不仅快而且不会给内存带来压力,因为我们所需要的值都是动态生成的而不是将他们存储在一个列表中。更概括的说如果现在我们在上面的例子中使用yield便可获得了一个协程。协程会消费掉发送给它的值。Python实现的grep就是个很好的例子:

    def grep(pattern):print("Searching for", pattern)while True:line = (yield)if pattern in line:print(line) 

等等!yield返回了什么?啊哈,我们已经把它变成了一个协程。它将不再包含任何初始值,相反要从外部传值给它。我们可以通过send()方法向它传值。这有个例子:

    search = grep('coroutine')next(search)#output: Searching for coroutinesearch.send("I love you")search.send("Don't you love me?")search.send("I love coroutine instead!")#output: I love coroutine instead!

发送的值会被yield接收。我们为什么要运行next()方法呢?这样做正是为了启动一个协程。就像协程中包含的生成器并不是立刻执行,而是通过next()方法来响应send()方法。因此,你必须通过next()方法来执行yield表达式。

我们可以通过调用close()方法来关闭一个协程。像这样:

    search = grep('coroutine')search.close()

更多协程相关知识的学习大家可以参考David Beazley的这份精彩演讲。

内存copy

id

什么是id?一个对象的id值在CPython解释器里就代表它在内存中的`地址

>>> import copy
>>> a=[1,2,3]
>>> b=a
>>> id(a)
"""
4382960392
"""
>>> id(b)
"""
4382960392
"""
>>> id(a)==id(b)    #附值后,两者的id相同,为true。
True
>>> b[0]=222222  #此时,改变b的第一个值,也会导致a值改变。
>>> print(a,b)
[222222, 2, 3] [222222, 2, 3] #a,b值同时改变

##浅拷贝

当使用浅拷贝时,python只是拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。看代码:

>>> import copy
>>> a=[1,2,3]
>>> c=copy.copy(a)  #拷贝了a的外围对象本身,
>>> id(c)
4383658568
>>> print(id(a)==id(c))  #id 改变 为false
False
>>> c[1]=22222   #此时,我去改变c的第二个值时,a不会被改变。
>>> print(a,c)
[1, 2, 3] [1, 22222, 3] #a值不变,c的第二个值变了,这就是copy和‘==’的不同

##深拷贝

deepcopy对外围和内部元素都进行了拷贝对象本身,而不是对象的引用。

#copy.copy()>>> a=[1,2,[3,4]]  #第三个值为列表[3,4],即内部元素
>>> d=copy.copy(a) #浅拷贝a中的[3,4]内部元素的引用,非内部元素对象的本身
>>> id(a)==id(d)
False
>>> id(a[2])==id(d[2])
True
>>> a[2][0]=3333  #改变a中内部原属列表中的第一个值
>>> d             #这时d中的列表元素也会被改变
[1, 2, [3333, 4]]#copy.deepcopy()>>> e=copy.deepcopy(a) #e为深拷贝了a
>>> a[2][0]=333 #改变a中内部元素列表第一个的值
>>> e
[1, 2, [3333, 4]] #因为时深拷贝,这时e中内部元素[]列表的值不会因为a中的值改变而改变
>>>

多进程

本节我们来学习threading模块的一些基本操作,如获取线程数,添加线程等。首先别忘了导入模块:

import threading
获取已激活的线程数
threading.active_count()

查看所有线程信息

threading.enumerate()
# [<_MainThread(MainThread, started 140736011932608)>, <Thread(SockThread, started daemon 123145376751616)>]
输出的结果是一个<_MainThread(...)>带多个<Thread(...)>。

查看现在正在运行的线程

threading.current_thread()
# <_MainThread(MainThread, started 140736011932608)>

添加线程,threading.Thread()接收参数target代表这个线程要完成的任务,需自行定义

def thread_job():print('This is a thread of %s' % threading.current_thread())def main():thread = threading.Thread(target=thread_job,)   # 定义线程 thread.start()  # 让线程开始工作if __name__ == '__main__':main()

join

我们让 T1 线程工作的耗时增加.

import threading
import timedef thread_job():print("T1 start\n")for i in range(10):time.sleep(0.1) # 任务间隔0.1sprint("T1 finish\n")added_thread = threading.Thread(target=thread_job, name='T1')
added_thread.start()
print("all done\n")

预想中输出的结果是否为:

T1 start
T1 finish
all done

但实际却是:

T1 start
all done
T1 finish

线程任务还未完成便输出all done。如果要遵循顺序,可以在启动线程后对它调用join:

added_thread.start()
added_thread.join()
print("all done\n")

使用join对控制多个线程的执行顺序非常关键。举个例子,假设我们现在再加一个线程T2,T2的任务量较小,会比T1更快完成:

def T1_job():print("T1 start\n")for i in range(10):time.sleep(0.1)print("T1 finish\n")def T2_job():print("T2 start\n")print("T2 finish\n")thread_1 = threading.Thread(target=T1_job, name='T1')
thread_2 = threading.Thread(target=T2_job, name='T2')
thread_1.start() # 开启T1
thread_2.start() # 开启T2
print("all done\n")

输出的”一种”结果是:

T1 start
T2 start
T2 finish
all done
T1 finish

现在T1和T2都没有join,注意这里说”一种”是因为all done的出现完全取决于两个线程的执行速度, 完全有可能T2 finish出现在all done之后。这种杂乱的执行方式是我们不能忍受的,因此要使用join加以控制。

我们试试在T1启动后,T2启动前加上thread_1.join():

thread_1.start()
thread_1.join() # notice the difference!
thread_2.start()
print("all done\n")

输出结果:

T1 start
T1 finish
T2 start
all done
T2 finish

可以看到,T2会等待T1结束后才开始运行。

如果我们在T2启动后放上thread_1.join()会怎么样呢?

thread_1.start()
thread_2.start()
thread_1.join() # notice the difference!
print("all done\n")

输出结果:

T1 start
T2 start
T2 finish
T1 finish
all done

T2在T1之后启动,并且因为T2任务量小会在T1之前完成;而T1也因为加了join,all done在它完成后才显示。

你也可以添加thread_2.join()进行尝试,但为了规避不必要的麻烦,推荐如下这种1221的V型排布:

thread_1.start() # start T1
thread_2.start() # start T2
thread_2.join() # join for T2
thread_1.join() # join for T1
print("all done\n")"""
T1 start
T2 start
T2 finish
T1 finish
all done
"""

##储存进程结果

import threading
import time
from queue import Queuedef job(l,q):for i in range(len(l)):l[i] = l[i]**2q.put(l)def multithreading():q = Queue()threads = []data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]for i in range(4):t = threading.Thread(target=job, args=(data[i], q))t.start()threads.append(t)for thread in threads:thread.join()results = []for _ in range(4):results.append(q.get())print(results)if __name__ == '__main__':multithreading()

多线程的lock

类似golang里面的lock,推荐大家用协程,或者多进程解决这个问题。

import threadingdef job1():global A, locklock.acquire()for i in range(10):A += 1print('job1', A)lock.release()def job2():global A, locklock.acquire()for i in range(10):A += 10print('job2', A)lock.release()if __name__ == '__main__':lock = threading.Lock()A = 0t1 = threading.Thread(target=job1)t2 = threading.Thread(target=job2)t1.start()t2.start()t1.join()t2.join()

相关文章:

  • AI大模型面试大纲
  • Flutter中使用FFI的方式链接C/C++的so库(harmonyos)
  • 万象奥科工业平板上线,邀您体验与众不同!
  • 聊一下数据脱敏
  • 【机器学习(五)】分类和回归任务-AdaBoost算法
  • webpack 4 的 30 个步骤构建 react 开发环境
  • .NET CORE程序发布IIS后报错误 500.19
  • 嵌入式必懂微控制器选型:STM32、ESP32、AVR与PIC的比较分析
  • 银河麒麟,apt 安装软件报错640Unknown Status
  • JUC高并发编程5:多线程锁
  • 滚雪球学Oracle[7.1讲]:Oracle云数据库
  • Android Studio | 无法识别Icons.Default.Spa中的Spa
  • 实用工具推荐---- PDF 转换
  • AtCoder ABC371 A-D题解
  • 微信小程序处理交易投诉管理,支持多小程序
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 230. Kth Smallest Element in a BST
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • avalon2.2的VM生成过程
  • Fundebug计费标准解释:事件数是如何定义的?
  • JavaScript创建对象的四种方式
  • Puppeteer:浏览器控制器
  • Python 反序列化安全问题(二)
  • Python十分钟制作属于你自己的个性logo
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 关于Java中分层中遇到的一些问题
  • 缓存与缓冲
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • ​如何在iOS手机上查看应用日志
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • #define 用法
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (libusb) usb口自动刷新
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (含笔试题)深度解析数据在内存中的存储
  • (计算机网络)物理层
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (四)汇编语言——简单程序
  • (转)使用VMware vSphere标准交换机设置网络连接
  • ./configure,make,make install的作用
  • ./和../以及/和~之间的区别
  • .htaccess 强制https 单独排除某个目录
  • .net 获取url的方法
  • .net 受管制代码
  • .Net6 Api Swagger配置
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .Net的C#语言取月份数值对应的MonthName值
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .Net中wcf服务生成及调用
  • /usr/bin/perl:bad interpreter:No such file or directory 的解决办法