1.join的作用
import time
while True:
try:
print('hahaha,有本事,你让我结束啊')
time.sleep(1)
except KeyboardInterrupt:
pass
import multiprocessing
import time
def task():
for i in range(5):
print('hahhaha')
time.sleep(1)
p=ltiprocessing.Process(target=task)
p.start()
print('-----1-----')
p.join()
print('------2------')
while True:
print('xxxxxxxx')
time.sleep(1)
2.创建进程有3中方式:
1.直接创建Process对象,用traget指定函数,这个函数结束新的进程执行的代码
2.自定义一个类,继承Process,一定要重写run方法
3.进程池,Pool(3)
3.进程间通信-Queue
import os,time,random
from multiprocessing import Manager,Pool
def reader(q):
print('reader启动(%s),父进程为(%s)'%(os.getpid(),os.getppid()))
for i in range(q.qsize()):
print('reader从Queue获取到消息:%s'%q.get(True))
def writer(q):
print('writer启动(%s),父进程为(%s)'%(os.getpid(),os.getppid()))
for i in 'itcast':
q.put(i)
if __name__='__main__':
print('(%s)start'%os.getpid())
q=Manager().Queue()
po=Pool()
po.apply_async(writer,(q,))
time.sleep(1)
po.apply_async(reader,(q,))
po.close()
po.join()
print('(%s)End'%os.getpid())
4.文件夹copy器(多进程版)
import multiprocessing
import os
#7.定义一个函数,当做进程池中进程执行的代码
def copy_file(name,folder_name,dest_folder_name);
#7.1使用open打开一个文件,用来读取内容
with open(folder_name+'/'+name,'rb') as f:
content=f.read()
#7.2使用open新建一个文件,用来存储新的文件内容
with open(dest_folder_name+'/'+name,'wb') as f:
f.write(content)
def main():
#1.获取用户需要复制的文件夹
folder_name=input('请输入你要复制的文件夹的名字:')
#8.定义一个变量用存储目标文件夹的名字
dest_folder_name=folder_name+'[复件]'
os.mkdir(dest_folder_name)
#2.通过listdir获取指定的文件夹下的所有的文件名字
file_names=os.listdir(folder_name)
print(file_names)
#3.创建一个进程池
pool=multiprocessing.Pool(3)
#4.循环的方式向进程池中添加任务
#复制文件
for name in file_names:
pool.apply.async(copy_file,(name,folder_name,dest_folder_name))
#5.进程池关闭
pool.close()
#6.等待进程池中所有的进程结束
pool.join()
if __name__='__main__':
main()
import multiprocessing
import os
import time
import random
#7.定义一个函数,当做进程池中进程执行的代码
def copy_file(q,name,folder_name,dest_folder_name);
time.sleep(random.randint(0,2))
#7.1使用open打开一个文件,用来读取内容
with open(folder_name+'/'+name,'rb') as f:
content=f.read()
#7.2使用open新建一个文件,用来存储新的文件内容
with open(dest_folder_name+'/'+name,'wb') as f:
f.write(content)
#10.复制完成以后向Queue中写入一个名字,表示已经复制完成了
q.put(name)
def main():
#1.获取用户需要复制的文件夹
folder_name=input('请输入你要复制的文件夹的名字:')
#8.定义一个变量用存储目标文件夹的名字
dest_folder_name=folder_name+'[复件]'
try:
os.mkdir(dest_folder_name)
except:
pass
#2.通过listdir获取指定的文件夹下的所有的文件名字
file_names=os.listdir(folder_name)
#print(file_names)
#9.创建一个Queue
q=multiprocessing.Manager().Queue()
#3.创建一个进程池
pool=multiprocessing.Pool(3)
#4.循环的方式向进程池中添加任务
#复制文件
for name in file_names:
pool.apply.async(copy_file,(q,name,folder_name,dest_folder_name))
#11.主进程从Queue中获取已经复制完成的文件名
for i in range(len(file_names):
file_name=q.get()
print('\r当前的进度是:%.2f已经复制完成的文件名是:%s%s'%(i+1/len(file_names)*100,file_name,' '*30),end=' ')
print()
#5.进程池关闭
pool.close()
#6.等待进程池中所有的进程结束
pool.join()
if __name__='__main__':
main()
5.协程
5.1 协程是啥
协程是python中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。为啥说它是一个执行单元,因为它自带cpu上下文,这样只要在合法的时机,我们可以把一个协程切换到另一个协程。只要这个过程中保存或恢复cpu上下文那么程序还是可以运行的。
通俗的理解:在一个线程中某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行。注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。
5.2协程和线程差异
在实现多任务时,线程切换从系统层面远不止保存和恢复cpu上下文那么简单,操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮您做这些数据的恢复操作,所以线程的切换非常耗性能。但是协程的切换只是单纯的操作cpu的上下文,所以一秒钟切换个上百万次系统都扛得住
计算密集型任务:使用多进程
IO密集型任务:使用多线程,多协程
一个程序,为了完成多任务,可以由多进程来实现,假如说进程数是,那么此时有个一起运行。一个进程中可以开线程,假如每个进程有个线程,那么一共是10x10--->100个任务一起运行。一个线程中可以开协程,假如每个线程中有10个协程,那么一共是10x10x10---->1000个任务一起运行
6.使用yield实现协程
import time
def work1():
while True;
print('-----work1------')
yield
time.sleep(0.5)
def work2();
while True:
print('-----work2------')
yield
time.sleep(0.5)
def main():
w1=work1()
w2=work2()
while True:
next(w1)
next(w2)
if __name__='__main__':
main()
#通过协程能够实现多任务,但是它的这种方案一定是假的多任务,又因为只要运行时切换任务足够快,用户看不出来区别,所以表面上就是多任务
7.使用greenlet实现协程
from greenlet import greenlet
import time
def test1():
while True:
print('----A----')
gr2.switch()
time.sleep(0.5)
def test2():
while True:
print('-----B----')
gr1.switch()
time.sleep(0.5)
gr1=greenlet(test1)
gr2=greenlet(test2)
#切换到gr1中运行
gr1.switch()
#协程与进程,线程的不同
#1.进程,线程创建完成之后,到底是哪个进程,线程执行不确定,这要让操作系统来进行计算(调度算法,例如优先级调度)
#2.协程是可以人为来控制的
8.使用gevent实现协程
import gevent
import time
from gevent import monkey
monkey.path_all() #这句话一定要放到使用time等耗时操作的前面,它最后的效果是将time模块中的延时全部替换为gevent中的延时
#time模块中的延时是不具备自动切换任务的功能,而gevent中的延时具备,因此我们需要将time全部改为gevent,为了更快速的让本.py中的所有time变为gevent所以我们需要执行monkey.path_all()
def f1(n);
for i in range(n):
#print(gevent.getcurrent(),i)
print(-----f1------),i)
#gevent.sleep(1)
time.sleep(1)
def f2(n);
for i in range(n):
print(-----f2------),i)
#gevent.sleep(1)
time.sleep(1)
def f3(n);
for i in range(n):
print(-----f3------),i)
#gevent.sleep(1)
time.sleep(1)
g1=gevent.spawn(f1,5)
g2=gevent.spawn(f2,5)
g3=gevent.spawn(f3,5)
g1.join() #join会等待g1标识的那个任务执行完毕之后,对其进行清理工作,其实这就是一个耗时操作
g2.join()
g3.join()
#使用gevent来实现多任务的时候,有一个很特殊的地方
#它可以自行切换协程指定的任务,而且切换的前提是:当一个任务用到耗时 操作(例如延时),它就会把这个时间拿出来去做另外的任务
#这样做最终实现了多任务 而且自动切换
from greenlet import greenlet
import gevent
import random
import time
from gevent import monkey
monkey.path_all()
def coroutine_work1(coroutine_name):
for i in range(10):
print(coroutine_name,i)
time.sleep(random.random())
def coroutine_work2(coroutine_name):
for i in range(10):
print(coroutine_name,i)
time.sleep(random.random())
gevent.joinall([
gevent.spawn(coroutine_work,'work1'),
gevent.spawn(coroutine_work,'work2')
])
9.进程,线程,协程的对比
9.1简单总结
1.进程是资源分配的单位
2.线程是操作系统调度的单位
3.进程切换需要的资源最大,效率很低
4.线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
5.协程切换任务资源很小,效率高
6.多进程,多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中所以是并发