(并发编程线程)
进程部分: 1. 进程间通信=>IPC 2. 生产者消费者模型(*****) 线程部分: 1. 线程理论(*****) 2. 开启线程的两种方式(*****) 3. 线程对象其他相关的属性或方法 4. 守护线程 5. 线程互斥锁 6. GIL全局解释器锁(******) 7. 死锁与递归锁 1.进程间通信 进程之间通信必须找到一种介质,该介质必须满足 1、是所有进程共享的 2、必须是内存空间 附加:帮我们自动处理好锁的问题 a、from multiprocessing import Manager(共享内存,但要自己解决锁的问题) b、IPC中的队列(Queue) 共享,内存,自动处理锁的问题(最常用) c、IPC中的管道(Pipe),共享,内存,需自己解决锁的问题 a、用Manager from multiprocessing import Process,Manager,Lock import time mutex=Lock() def task(dic,lock): lock.acquire() temp=dic['num'] time.sleep(0.1) dic['num']=temp-1 lock.release() if __name__ == '__main__': m=Manager() dic=m.dict({'num':10}) l=[] for i in range(10): p=Process(target=task,args=(dic,mutex)) l.append(p) p.start() for p in l: p.join() print(dic) b、用队列Queue 1)共享的空间 2)是内存空间 3)自动帮我们处理好锁定问题 from multiprocessing import Queue q=Queue(3) #设置队列中maxsize个数为三 q.put('first') q.put({'second':None}) q.put('三') # q.put(4) #阻塞。不报错,程序卡在原地等待队列中清出一个值。默认blok=True print(q.get()) print(q.get()) print(q.get()) 强调: 1、队列用来存成进程之间沟通的消息,数据量不应该过大 2、maxsize的值超过的内存限制就变得毫无意义 了解: q=Queue(3) q.put('first',block=False) q.put('second',block=False) q.put('third',block=False) q.put('fourth',block=False) #报错 queue.Full q.put('first',block=True) q.put('second',block=True) q.put('third',block=True) q.put('fourth',block=True,timeout=3) #等待3秒后若还进不去报错。注意timeout不能和block=False连用 q.get(block=False) q.get(block=False) q.get(block=False) q.get(block=False) #报错 queue.Empty q.get(block=True) q.get(block=True) q.get(block=True) q.get(block=True,timeout=2) #等待2秒后还取不出东西则报错。注意timeout不能和block=False连用 2.生产者消费者模型 该模型中包含两类重要的角色: 1、生产者:将负责造数据的任务比喻为生产者 2、消费者:接收生产者造出的数据来做进一步的处理,该类人物被比喻成消费者 实现生产者消费者模型三要素 1、生产者 2、消费者 3、队列 什么时候用该模型: 程序中出现明显的两类任何,一类任务是负责生产,另外一类任务是负责处理生产的数据的 该模型的好处: 1、实现了生产者与消费者解耦和 2、平衡了生产者的生产力与消费者的处理数据的能力 import time import random from multiprocessing import Process,Queue def consumer(name,q): while True: res=q.get() time.sleep(random.randint(1,3)) print('\033[46m消费者===》%s 吃了 %s\033[0m' %(name,res)) def producer(name,q,food): for i in range(5): time.sleep(random.randint(1,2)) res='%s%s' %(food,i) q.put(res) print('\033[45m生产者者===》%s 生产了 %s\033[0m' %(name,res)) if __name__ == '__main__': #1、共享的盆 q=Queue() #2、生产者们 p1=Process(target=producer,args=('egon',q,'包子')) p2=Process(target=producer,args=('刘清政',q,'泔水')) p3=Process(target=producer,args=('杨军',q,'米饭')) #3、消费者们 c1=Process(target=consumer,args=('alex',q)) c2=Process(target=consumer,args=('梁书东',q)) p1.start() p2.start() p3.start() c1.start() c2.start() 生产者消费者模型是解决问题的思路不是技术。可以用进程和队列来实现,也可以用其他的来实现。 并发编程之线程 多任务可以由多进程完成,也可以由一个进程内的多线程完成。 我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。 由于线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程。 Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。 启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行: 一、线程理论 1、什么是线程 线程指的是一条流水线的工作过程 进程不是执行单位,是资源单位 一个进程内自带一个线程,线程是执行单位 2、进程VS线程 1、同一进程内的线程们共享该进程内资源,不同进程内的线程资源肯定是隔离的 2、创建线程的开销比创建进程要小的多 3、线程中没有父子关系。相较于子线程、主线程特殊之处在于其代变了主进程的生命周期。 主进程等待子进程结束然后结束,是为子进程回收资源。 主线程等待子线程结束然后结束,是等待这个进程的代码(其他非守护线程)执行完毕。 主进程:执行完代码就结束。 主线程:所以子线程结束才结束。 二、开启线程的两种方式 方式一:导入Thread模块 from threading import Thread import time def task(name): print('%s is running' %name) time.sleep(3) if __name__ == '__main__': t=Thread(target=task,args=('egon',)) t.start() print('主线程') 方式二:创建类继承Thread from threading import Thread import time class MyThread(Thread): def run(self): print('%s is running' %self.name) time.sleep(3) if __name__ == '__main__': t=MyThread() t.start() print('主线程') 三、进程vs线程 1、瞅一瞅PID (Process ID) from threading import Thread import time,os def task(): print('%s is running' %os.getpid()) time.sleep(3) if __name__ == '__main__': t=Thread(target=task,) t.start() print('主线程',os.getpid()) #一个进程中的子线程pid相同 2、线程创建开销小 3、同一进程内的多个线程共享该进程内的资源 from threading import Thread import time,os x=1000 def task(): global x x=0 if __name__ == '__main__': t=Thread(target=task,) t.start() t.join() print('主线程',x) #主线程 0 四、线程对象的其他方法 from threading import Thread,current_thread,active_count,enumerate import time,os def task(): print('%s is running' %current_thread().name) #Thread-1 is running time.sleep(3) if __name__ == '__main__': t1=Thread(target=task,name='第一个线程') t2=Thread(target=task,) t3=Thread(target=task,) t1.start() t2.start() t3.start() print(t1.is_alive()) #True print(active_count()) #4 print(enumerate()) #[<_MainThread(MainThread, started 4320768832)>, <Thread(第一个线程, started 123145551912960)>, <Thread(Thread-1, started 123145557168128)>, <Thread(Thread-2, started 123145562423296)>] #当前活跃的线程 print('主线程',current_thread().name) #主线程 MainThread 五、守护线程 from threading import Thread,current_thread import time def task(): print('%s is running' %current_thread().name) time.sleep(3) if __name__ == '__main__': t1=Thread(target=task,name='第一个线程') t1.daemon = True t1.start() print('主线程') from threading import Thread import time def foo(): print(123) time.sleep(5) print("end123") def bar(): print(456) time.sleep(3) print("end456") if __name__ == '__main__': t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------") ''' 123 456 main------- end456 ''' 主进程:执行完代码就结束。 主线程:所以子线程结束才结束。 总结:只要进程内没有可执行的代码守护就结束 六、线程互斥锁 from threading import Thread,Lock import time mutex=Lock() x=100 def task(): global x mutex.acquire() temp=x time.sleep(0.1) x=temp-1 mutex.release() if __name__ == '__main__': start=time.time() t_l=[] for i in range(100): t=Thread(target=task) t_l.append(t) t.start() for t in t_l: t.join() print('主',x) #0 print(time.time()-start)