当前位置: 首页 > 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()

http://www.mrgr.cn/news/40219.html

相关文章:

  • OpenStack系列第一篇:深入了解虚拟化技术与环境搭建
  • 【网络安全】内部应用中的多重漏洞利用
  • 如何使用ChatGPT API及Bito插件
  • leetcode274. H 指数
  • 基于投影滤波算法的rick合成地震波滤波matlab仿真
  • springboot整合seata
  • lambda表达式底层实现
  • 基于深度学习的持续的知识积累与转移
  • JDK9与JDK8对比
  • 【计算机毕业设计】springboot就业信息管理系统
  • 计算机毕业设计 服装生产信息管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解
  • 回归预测 | Matlab基于POA-SVR鹈鹕算法优化支持向量机的数据多输入单输出回归预测
  • [Uninstall] 软件彻底卸载工具的下载及详细安装使用过程(附有下载文件)
  • Unity实战案例全解析:RTS游戏的框选和阵型功能(5)阵型功能 优化
  • Unity实战案例全解析:RTS游戏的框选和阵型功能 总结
  • 【小程序】小tips:微信小程序登录后返回登录前的页面继续操作(保留参数)
  • 心理咨询预约管理系统(含源码+sql+视频导入教程)
  • C语言——动态内存分配
  • 数据预处理:数据挖掘的第一步
  • LORA模型与基座大模型合并并由transformer的AutoModel推理