python 四——线程、进程、协程
内容概要
1、进程与线程优、缺点的比较
2、适用情况
3、线程
线程的创建
setDaemon
join
event
RLock
队列
4、进程
创建进程
setDaemon
join
线程与进程,数据之间是否共享对比
特殊的数据容器
进程池
5、协程
1、进程与线程优、缺点的比较
总言:使用进程和线程的目的,提高执行效率。
进程:
优点:能利用机器的多核性能,同时进行多个操作。
缺点:需要耗费资源,重新开辟内存空间,耗内存。
线程:
优点:共享内存(资源),做IO操作时,可以创造并发操作。
缺点:抢占资源。
总结:进程并不是越多越好,最好CPU个数 = 进程个数。
线程也并不是越多越好,应根据业务需求来确定个数,因为请求上下文切换非常耗时。
2、适用情况
IO密集型(不用CPU) :适合多线程
计算密集型(要用CPU):适合多进程
3、线程
(1)线程的创建(threading模块)
1 import threading #导入该模块 2 import time 3 4 def f0(): 5 pass 6 7 def f1(a1,a2): 8 time.sleep(1) 9 print(a1,a2) 10 f0() 11 12 #创建子线程,任务为f1(),参数为args的元祖 13 t = threading.Thread(target=f1,args=(123,456,)) 14 #默认setDaemon为false,主线程要等待子线程执行完毕后再结束 15 #设置为True后,就不等待 16 t.setDaemon(True) 17 t.start() #告诉线程我们准备好了
(2)主线程是否等待子线程
t.setDaemon(True/False)
用于设置主线程执行完毕后,是否等待子线程,默认为false,要等待。
(3)主线程是否等待某个子线程执行完毕
t.join() 一直等待
t.join(2)最多等待该子线程2s
(4)线程锁RLock
避免因并发操作而造成脏数据,线程锁能锁住全部子线程,同一时刻允许一个线程执行操作。
1 #未使用线程锁时 2 import threading 3 import time 4 5 gl_num = 0 6 7 def show(arg): 8 global gl_num 9 time.sleep(0.5) 10 gl_num +=1 11 print(gl_num) 12 13 #开了10个线程,同时都对全局变量gl_num进行操作 14 for i in range(10): 15 t = threading.Thread(target=show, args=(i,)) 16 t.start() 17 18 print('main thread stop')
1 #使用了线程锁时 2 import threading 3 import time 4 5 gl_num = 0 6 7 lock = threading.RLock() #创建锁 8 9 def Func(): 10 lock.acquire() #锁定 11 global gl_num 12 gl_num += 1 13 time.sleep(0.25) 14 print(gl_num) 15 lock.release() #释放锁 16 17 for i in range(10): 18 t = threading.Thread(target=Func) 19 t.start() 20 print('main thread stop')
(5)事件(Event)
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
event.wait() 等待绿灯开启,再继续执行
event.clear()设为红灯
event.set() 设为绿灯
1 import threading 2 3 def do(event): 4 print('start') 5 event.wait() #阻塞住,等待绿灯。event_obj.set()语句执行后,又回来继续执行下一句 6 print('execute') 7 8 event_obj = threading.Event() 9 for i in range(3): 10 t = threading.Thread(target=do, args=(event_obj,)) 11 t.start() 12 13 event_obj.clear() #设为红灯 14 inp = input('input:') 15 if inp == 'true': 16 event_obj.set() #设为绿灯
执行结果:
1 start 2 start 3 start 4 input:true 5 execute 6 execute 7 execute
(6)生产者消费者模型、队列(先进先出)(queue模块)
4、进程
(1)创建进程(multiprocessing模块)
1 import multiprocessing 2 import time 3 4 def f1(a1): 5 time.sleep(3) 6 print(a1) 7 8 if __name__ == '__main__': #Windows上,进程语句必须放在main里面 9 #创建进程,任务为执行f1(),参数为11,由元祖封装 10 t = multiprocessing.Process(target=f1,args=(11,)) 11 # 当daemon设为true时,主进程结束后就不再等待子进程了,默认为false要等待 12 t.daemon = True #所以结果没有打印出 11 13 t.start() 14 15 t = multiprocessing.Process(target=f1,args=(22,)) 16 t.start() 17 print('end')
(2)主进程是否等待子进程
t.setDaemon(True/False)
用于设置主进程执行完毕后,是否等待子进程,默认为false,要等待。
(3)主进程是否等待某个子进程执行完毕
t.join() 一直等待
t.join(2)最多等待该子进程2s
(4)线程与进程,数据之间是否共享对比
默认每个进程之间的数据是不共享的,各做各的。
而每个线程之间的数据是共享的。
1 #进程操作时,数据是不共享的 2 from multiprocessing import Process 3 4 li = [] 5 def foo(i): 6 li.append(i) 7 print('say hi',li) 8 9 if __name__ == '__main__': 10 for i in range(10): 11 p = Process(target=foo,args=(i,)) 12 p.start()
结果为:
1 say hi [0] 2 say hi [2] 3 say hi [1] 4 say hi [3] 5 say hi [4] 6 say hi [5] 7 say hi [6] 8 say hi [7] 9 say hi [9] 10 say hi [8]
线程处理时,数据是共享的,将process改为thread,结果为:
1 say hi [0] 2 say hi [0, 1] 3 say hi [0, 1, 2] 4 say hi [0, 1, 2, 3] 5 say hi [0, 1, 2, 3, 4] 6 say hi [0, 1, 2, 3, 4, 5] 7 say hi [0, 1, 2, 3, 4, 5, 6] 8 say hi [0, 1, 2, 3, 4, 5, 6, 7] 9 say hi [0, 1, 2, 3, 4, 5, 6, 7, 8] 10 say hi [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(5)特殊的数据容器
如果想要多个进程同时操作一份数据,则需要特殊的容器。
方法一:Array数组(不推荐)
a.创建时需要规定大小,切不能改变。
b.内部数据必须统一为相同类型,字符串、数字等。
方法二:manager.dict()共享数据(推荐)
创建: m = Manager()
dict = m.dict()
1 from multiprocessing import Process,Manager 2 3 def Foo(i,dic): 4 dic[i] = 100+i 5 print(len(dic)) 6 7 # for k,v in dic.items(): 8 # print(k,v) 9 if __name__ == '__main__': 10 manager = Manager() 11 dic = manager.dict() 12 # dic = {} #dic为普通字典时,返回值为 1 1 13 14 for i in range(2): 15 p = Process(target=Foo,args=(i,dic)) 16 p.start() 17 p.join()
返回值为:1 2
(6)进程池(python中已创建好) pool
1 from multiprocessing import Pool 2 import time 3 4 def f1(a1): 5 time.sleep(1) 6 print(a1) 7 return 1000 8 9 def f2(arg): 10 print(arg) 11 12 if __name__ == '__main__': 13 14 pool = Pool(5) 15 # pool.apply(f1,(2,)) 16 for i in range(10): 17 pool.apply_async(func=f1, args=(i,), callback=f2)#特别注意args跟的参数为元祖类型 18 #callback=f2表示回调函数,将f1 return的值作为参数传给f2 19 pool.close() 20 pool.join()#等待子进程执行完
pool.apply()和pool.apply_async()对比:
pool.apply() :每一个任务都是排队执行的,内部有join()方法,会等待子进程
pool.apply_async():每一个任务都是并发执行,且可以设置回调函数,内部无join()方法,
进程deamon = true,不等待子进程,要想等待子进程,需先pool.close(),再pool.join()
5、协程(高性能代名词)
线程和进程的操作时程序出发系统接口,最后的执行者是系统,协程的操作则是程序员。
存在意义:只使用一个线程,在一个线程中规定某个代码块执行顺序。
适用于: IO密集型操作
方法一:greenlet模块
需手动切换任务(不推荐)
方法二:gevent模块(本质也是基于greenlet)
自动切换任务,谁先回来就先处理谁(推荐)
1 import gevent 2 3 def foo(): 4 print('1') 5 gevent.sleep(0) #切换标志 6 print('2') 7 8 9 def bar(): 10 print('3') 11 gevent.sleep(0) #切换标志 12 print('4') 13 14 15 gevent.joinall([ 16 gevent.spawn(foo), 17 gevent.spawn(bar), 18 ])
结果为: 1 3 2 4
补充知识点:http://www.cnblogs.com/wupeiqi/articles/6229292.html