并发编程——多进程

---恢复内容开始---python

本节导读:web

  • multiprocessing模块介绍
  • process类的介绍
  • 开启进程的两种方式
  • join方法
  • 守护进程
  • 互斥锁
  • 队列
  • 生产者与消费者模型

 

一 multiprocessing模块介绍编程

  python中的多线程没法利用多核优点,若是想要充分地使用多核CPU的资源(os.cpu\_count\(\)查看),在python中大部分状况须要使用多进程。windows

Python提供了multiprocessing。 multiprocessing模块用来开启子进程,并在子进程中执行咱们定制的任务(好比函数),该模块与多线程模块threading的编程接口相似。multiprocessing模块的功能众多:支持子进程、通讯和共享数据、执行不一样形式的同步,>提供了Process、Queue、Pipe、Lock等组件。安全

须要再次强调的一点是:与线程不一样,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。多线程

二 Process类的介绍并发

建立进程app

Process([group [, target [, name [, args [, kwargs]]]]])
#由该类实例化获得的对象,可用来开启一个子进程

强调:
1. 须要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

参数介绍dom

#group参数未使用,值始终为None
#target表示调用对象,即子进程要执行的任务
#args表示调用对象的位置参数元组,args=(1,2,'egon',)
#kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
#name为子进程的名称

方法介绍ide

p.start()  #启动进程,并调用该子进程中的p.run() 
p.run() #进程启动时运行的方法,正是它去调用target指定的函数,咱们自定义类的类中必定要实现该方法  
p.terminate() #强制终止进程p,不会进行任何清理操做,若是p建立了子进程,该子进程就成了僵尸进程,使用该方法须要特别当心这种状况。若是p还保存了一个锁那么也将不会被释放,进而致使死锁
p.is_alive() #若是p仍然运行,返回True
p.join([timeout]) #主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间。

属性介绍

p.daemon  #默认值为False,若是设为True,表明p为后台运行的守护进程,当p的父进程终止时,p也随之终止,而且设定为True后,p不能建立本身的新进程,必须在p.start()以前设置
p.name #进程的名称
p.pid # 进程的pid

 三 开启进程的两种方式

注意:在windows中Process()必须放到# if __name__ == '__main__':下

方法一

import time
import random
from multiprocessing import Process

def piao(name):
    print('%s piaoing' %name)
    time.sleep(random.randrange(1,5))
    print('%s piao end' %name)

if __name__ == '__main__':
    #实例化获得四个对象
    p1=Process(target=piao,args=('egon',)) #必须加,号
    p2=Process(target=piao,args=('alex',))
    p3=Process(target=piao,args=('wupeqi',))
    p4=Process(target=piao,args=('yuanhao',))

    #调用对象下的方法,开启四个进程
    p1.start()
    p2.start()
    p3.start()
    p4.start()
    print('')
直接经过process实例化

 方法二

import time
import random
from multiprocessing import Process

class Piao(Process):
    def __init__(self,name):
        super().__init__()
        self.name=name
    def run(self):
        print('%s piaoing' %self.name)

        time.sleep(random.randrange(1,5))
        print('%s piao end' %self.name)

if __name__ == '__main__':
    #实例化获得四个对象
    p1=Piao('egon')
    p2=Piao('alex')
    p3=Piao('wupeiqi')
    p4=Piao('yuanhao')

    #调用对象下的方法,开启四个进程
    p1.start() #start会自动调用run
    p2.start()
    p3.start()
    p4.start()
    print('')
继承process类

四 join方法

在主进程运行过程当中若是想并发地执行其余的任务,咱们能够开启子进程,此时主进程的任务与子进程的任务分两种状况

状况一:在主进程的任务与子进程的任务彼此独立的状况下,主进程的任务先执行完毕后,主进程还须要等待子进程执行完毕,而后统一回收资源。

状况二:若是主进程的任务在执行到某一个阶段时,须要等待子进程执行完毕后才能继续执行,就须要有一种机制可以让主进程检测子进程是否运行完毕,在子进程执行完毕后才继续执行,不然一直在原地阻塞,这就是join方法的做用

from multiprocessing import Process
import time
import random
import os

def task():
    print('%s is piaoing' %os.getpid())
    time.sleep(random.randrange(1,3))
    print('%s is piao end' %os.getpid())

if __name__ == '__main__':
    p=Process(target=task)
    p.start()
    p.join() #等待p中止,才执行下一行代码
    print('')
join

 

五 守护进程

   若是咱们有两个任务须要并发执行,那么开一个主进程和一个子进程分别去执行就ok了,若是子进程的任务在主进程任务结束后就没有存在的必要了,那么该子进程应该在开启前就被设置成守护进程。主进程代码运行结束,守护进程随即终止

关于守护进程须要强调两点:

其一:守护进程会在主进程代码执行结束后就终止

 

其二:守护进程内没法再开启子进程,不然抛出异常:AssertionError: daemonic processes are not allowed to have children

开启守护进程方法:

from multiprocessing import Process
import time
import random

def task(name):
    print('%s is piaoing' %name)
    time.sleep(random.randrange(1,3))
    print('%s is piao end' %name)


if __name__ == '__main__':
    p=Process(target=task,args=('egon',))
    p.daemon=True #必定要在p.start()前设置,设置p为守护进程,禁止p建立子进程,而且父进程代码执行结束,p即终止运行
    p.start()
    print('') #只要终端打印出这一行内容,那么守护进程p也就跟着结束掉了
Daemon

六 互斥锁

  进程之间数据隔离,可是共享一套文件系统,于是能够经过文件来实现进程直接的通讯,而多个进程同时操做同一数据,必然会致使数据错乱,这时候就要经过加互斥锁来解决。

#由并发变成了串行,牺牲了运行效率,但避免了竞争
from multiprocessing import Process,Lock
import os,time
def work(lock):
    lock.acquire() #加锁
    print('%s is running' %os.getpid())
    time.sleep(2)
    print('%s is done' %os.getpid())
    lock.release() #释放锁
if __name__ == '__main__':
    lock=Lock()
    for i in range(3):
        p=Process(target=work,args=(lock,))
        p.start()
lock

 总结:

加锁能够保证多个进程修改同一块数据时,同一时间只能有一个任务能够进行修改,即串行地修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。

虽然能够用文件共享数据实现进程间通讯,但问题是:

一、效率低(共享数据基于文件,而文件是硬盘上的数据)

二、须要本身加锁处理

所以咱们最好找寻一种解决方案可以兼顾:

一、效率高(多个进程共享一块内存的数据)

二、帮咱们处理好锁问题。

这就是mutiprocessing模块为咱们提供的基于消息的IPC通讯机制:队列和管道。

队列和管道都是将数据存放于内存中,而队列又是基于(管道+锁)实现的,可让咱们从复杂的锁问题中解脱出来,于是队列才是进程间通讯的最佳选择。

咱们应该尽可能避免使用共享数据,尽量使用消息传递和队列,避免处理复杂的同步和锁问题,并且在进程数目增多时,每每能够得到更好的可获展性。

 

七 队列

进程彼此之间互相隔离,要实现进程间通讯(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的

队列的建立

 

from multiprocessing import Queue
q =Queue([maxsize])
#建立共享的进程队列,Queue是多进程安全的队列,可使用Queue实现多进程之间的数据传递。

 

参数介绍

maxsize是队列中容许最大项数,省略则无大小限制。
但须要明确:
    1、队列内存放的是消息而非大数据
    二、队列占用的是内存空间,于是maxsize即使是无大小限制也受限于内存大小

队列的使用

from multiprocessing import Process,Queue

q=Queue(3)

#put ,get ,put_nowait,get_nowait,full,empty
q.put(1)   #用以插入数据到队列中。
q.put(2)
q.put(3)
print(q.full()) #满了
# q.put(4) #再放就阻塞住了

print(q.get())     #能够从队列读取而且删除一个元素。

print(q.get())
print(q.get())
print(q.empty()) #空了
# print(q.get()) #再取就阻塞住了
队列使用

 

八 生产者与消费者模型

为何要使用生产者消费者模型

生产者指的是生产数据的任务,消费者指的是处理数据的任务,在并发编程中,若是生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。一样的道理,若是消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题因而引入了生产者和消费者模式。

什么是生产者和消费者模式

生产者消费者模式是经过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通信,而经过阻塞队列来进行通信,因此生产者生产完数据以后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就至关于一个缓冲区,平衡了生产者和消费者的处理能力。

这个阻塞队列就是用来给生产者和消费者解耦的

Queue([maxsize]):建立共享的进程队列,Queue是多进程安全的队列,可使用Queue实现多进程之间的数据传递。

参数介绍:

maxsize是队列中容许最大项数,省略则无大小限制。
但须要明确:
    一、队列内存放的是消息而非大数据
    二、队列占用的是内存空间,于是maxsize即使是无大小限制也受限于内存大小

主要方法介绍:

q.put方法用以插入数据到队列中。
q.get方法能够从队列读取而且删除一个元素。

队列的使用

from multiprocessing import Process,Queue

q=Queue(3)

#put ,get ,put_nowait,get_nowait,full,empty
q.put(1)
q.put(2)
q.put(3)
print(q.full()) #满了
# q.put(4) #再放就阻塞住了

print(q.get())
print(q.get())
print(q.get())
print(q.empty()) #空了
# print(q.get()) #再取就阻塞住了
相关文章
相关标签/搜索