并发编程在了解并发编程之前,我们需要了解一些排序,比如程序、进程、线程。程序:程序是代码,是用某种语言编写的命令的集合。进程:正在进行的进程或任务是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是资源分配的最小单位。Thread:比processes单元执行的进程子集,thread在一个进程中执行,thread是cpu调度的最小单位1.Process同一个程序被执行多次,也就是开启多个进程。流程中的两个概念是并行和并发的。并行是指同时运行和处理多个进程。任务需要多个CPU才能实现。并发是指处理多个任务。它似乎在同一时间运行。是伪并行1.1进程的一种状态。三种状态:就绪、运行、阻塞就绪:进程已经分配了除CPU以外的所有必要资源,只要获得处理器就可以立即执行。进程此时的状态称为就绪状态。程序正在处理器上执行,此时的进程状态称为执行阻塞状态:当正在执行的进程因等待一定时间而无法执行时,就会放弃处理器,处于阻塞状态,导致进程阻塞的时间可能有很多种,比如等待I/O完成,申请缓冲区不能满足,等待信件(信号)等。1.2开启多进程的两种方式python中的多进程模块在multiprocessing模块Process类classProcess:def__init__(self,group=None,target=None,name=None,args=(),kwargs={},*,daemon=None):"""group:不使用该参数,取值始终为Nonetarget:表示调用对象,即子进程要执行的任务args:表示位置参数调用对象的元组,args=(1,2,'egon',)kwargs:代表调用对象的字典,kwargs={'name':'egon','age':18}name是:名字thechildprocessdaemon:是否启用守护进程模式"""进程属性和方法方法说明p.start()启动进程,并在子进程中调用p.run()。p.join()主进程等待子进程终止。p.join只能加入mainstart启动的进程,不能加入mainrun开启的进程p.is_alive()验证进程是否还活着p.terminate()强制进程终止p.daemon守护进程过程。如果设置为True,则表示它是一个守护进程。当父进程结束时,子进程也结束。子流程不能创建自己的子流程。您必须在启动前设置p.name进程的名称和p.pid进程的pid。importtimefrommultiprocessingimportProcessdeftask(name):print(f'{name}isrunning')time.sleep(3)print(f'{name}isdone')if__name__=='__main__':p=Process(target=task,args=('process',))#创建线程p.start()#发送信号,申请内存空间,需要一定的时间print('mainprocess')#mainprocess#结果如下:"""主进程进程正在运行,进程完成"""方法2importtimefrommultiprocessingimportProcessclassOwnProcess(Process):def__init__(self,name):super().__init__()self.name=namedefrun(self):print(f'{self.name}正在运行')time.sleep(3)print(f'{self.name}isdone')if__name__=='__main__':p=OwnProcess('process')#创建进程p.start()#触发运行方法print('mainprocess')#结果如下:"""主进程正在运行,进程完成"""p.start()是启动进程的方式。1.3进程间数据隔离multiprocessingimportProcessimporttimen=100deftask():globalnn=0print(n)if__name__=='__main__':p=Process(target=task)p.start()print(p.is_alive())#验证进程是否还活着p.join()#等待子进程运行后执行以下代码print(p.is_alive())print('mainprocess',n)#结果:"""True0Falsemainprocess100"""1.4ZombieprocessandorphanprocessZombieprocess:指一个进程使用Fork创建一个子进程。如果子进程退出,父进程没有调用wait或waitpid获取子进程的状态信息,系统中仍然保存着子进程的进程描述符。这个进程称为死进程。孤儿进程:顾名思义,父进程结束,而子进程还活着。孤儿进程是无害的,并在用户机器结束时终止。它具有收养这些孤儿的功能。1.5守护进程daemon:当父进程代码运行代码后,子进程执行的任务不再需要,那么应该将子进程设置为守护进程importtimefrommultiprocessingimportProcessdeftask(name):print(f'{name}isrunning')time.sleep(5)print(f'{name}isdone')if__name__=='__main__':p=Process(target=task,args=('action',))p.daemon=True#创建守护进程p.start()time.sleep(2)print('mainprocess')#Result:"""actionisrunningmainprocess"""1.6Processmutex当两个或多个线程访问同一个同时获取资源,会出现一系列的问题,比如进程之间不共享数据,但是他们共享同一个文件系统,所以访问同一个文件,或者同一个打印终端,没有问题,但是共享带来竞争,竞争的结果是混乱,加锁可以解决这个问题h():"""查票"""time.sleep(random.randint(1,3))dic=json.load(open('db.txt','r',encoding='utf-8'))print(f'{os.getpid()}查看剩余票数{dic["count"]}')defget():"""购票"""dic=json.load(open('db.txt','r',encoding='utf-8'))如果dic['count']>0:dic['count']-=1time.sleep(random.randint(1,3))json.dump(dic,open('db.txt','w',encoding='utf-8'))print(f'{os.getpid()}购票成功')deftask(mutex):search()mutex.acquire()#lockget()mutex.release()#释放if__name__=='__main__':mutex=Lock()foriinrange(5):p=Process(target=task,args=(mutex,))p.start()print('主流程')#result:"""主流程7533查询剩余票数17535查询剩余票数17531查询剩余票数17532查询剩余票数17534查询剩余票数17533购票成功"""1.7进程间通信进程间的数据是相互隔离的。要实现进程间通信,必须要用到一些技术,比如在多进程模块中:队列和管道。这两种方式都可以实现进程间的数据传输。由于队列是通过管道+锁实现的,所以创建了共享进程队列。Queue是一个多进程安全队列。队列可以用来实现多个进程之间的数据传输。frommultiprocessingimportQueueq=Queue(3)q.put('first')q.put(2)q.put(['count',3])#q.put('fourth')#默认阻塞#q.put_nowait('fourth')#不阻塞#q.put('fourth',block=False)#不阻塞,报错#q.put('fourth',block=True,timeout=5)#设置超时print(q.get())print(q.get())print(q.get())#print(q.get())#默认阻塞#print(q.get_nowait())#默认非阻塞#print(q.get(block=False))#非阻塞#print(q.get(block=True,timeout=5))#设置超时ProducerModeConsumerModeProducerConsumerMode通过容器解决了生产者和消费者之间的强耦合问题。生产者和消费者之间并不直接通信,而是通过阻塞队列进行通信,所以生产者生产数据后,不需要等待消费者处理,而是直接丢到阻塞队列中,而消费者也不向生产者要数据,而是直接从阻塞队列中取importtimeimportrandomfrommultiprocessingimportQueue,Processdefproducer(name,food,q):foriinrange(3):res=f'{food}{i}'time.sleep(random.randint(1,3))打印(f'chef{name}produced{res}')#consumer('action',res)q.put(res)defconsumer(name,q):whileTrue:res=q.get()ifresisNone:breaktime.sleep(random.randint(1,3))print(f'Food{name}ate{res}')if__name__=='__main__':#Queueq=Queue()#Producerp1=Process(target=producer,args=('wu_chang1','hotpot',q))p2=Process(target=producer,args=('wu_chang2','hotpot',q))#consumerc1=Process(target=consumer,args=('action1',q))c2=Process(target=consumer,args=('action2',q))c3=Process(target=consumer,args=('action3',q))#开始线程p1.start()p2.start()c1.start()c2.start()c3.start()p1.join()p2.join()q.put(None)#发送结束信号q.put(None)#发送结束信号q.put(None)#发送结束信号c1.join()c2.join()c3.join()print('mainprocess')还有一个不好的就是每个进程都要发送结束信号,这对开发很不友好importtimeimportrandomfrommultiprocessingimportProcess,JoinableQueuedefproducer(name,food,q):foriinrange(3):res=f'{food}{i}'time.sleep(random.randint(1,3))打印(f'chef{name}produced{res}')#consumer('action',res)q.put(res)defconsumer(name,q):whileTrue:res=q.get()time.sleep(random.randint(1,3))print(f'foodie{name}ate{res}')q.task_done()if__name__=='__main__':#Queueq=JoinableQueue()#ProductionProducerp1=Process(target=producer,args=('wu_chang1','hotpot',q))p2=Process(target=producer,args=('wu_chang2','hotpot',q))#consumerc1=Process(target=消费者,args=('action1',q))c2=Process(target=consumer,args=('action2',q))c3=Process(target=consumer,args=('action3',q))c1.daemon=Truec2.daemon=Truec3.daemon=True#开启线程p1.start()p2.start()c1.start()c2.start()c3.start()p1.join()p2.join()q.join()print('mainprocess')将消费者设置为守护线程,当JoinableQueue队列中没有任何内容时,结束主进程
