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

posted @ 2017-10-25 11:34  MaryMaryTang  阅读(275)  评论(1编辑  收藏  举报