信号量,事件,队列

一:多进程方法汇总

# 多进程代码
# from multiprocessing import Process
# 方法
    # 进程对象.start()     开启一个子进程
    # 进程对象.join()      感知一个子进程的结束
    # 进程对象.terminate() 结束一个子进程
    # 进程对象.is_alive()  查看某个子进程是否还在运行
# 属性
    # 进程对象.name        进程名
    # 进程对象.pid         进程号
    # 进程对象.daemon      值为True的时候,表示新的子进程是一个守护进程
            # 守护进程 随着主进程代码的执行结束而结束
            # 一定在start之前设置


# from multiprocessing import Lock
# l = Lock()
# l.acquire()   # 拿钥匙
# 会造成数据不安全的操作
# l.release()   # 还钥匙

二:多进程补充知识:

from multiprocessing import Process
def func():
    num = input('>>>')
    print(num)

if __name__ == '__main__':
    Process(target=func).start()

    # 多进程中server端子进程不能直接input。否则报错。

三:

信号量 —— multiprocess.Semaphore
互斥锁同时只允许一个线程更改数据,而信号量Semaphore是同时允许一定数量的线程更改数据 。
假设商场里有4个迷你唱吧,所以同时可以进去4个人,如果来了第五个人就要在外面等待,
等到有人出来才能再进去玩。
实现:
信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,
acquire()调用被阻塞。这是迪科斯彻(Dijkstra)信号量概念P()和V()的Python实现。
信号量同步机制适用于访问像服务器这样的有限资源。
信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念

理解:就好像四个人一起去ktv,屋里只能有四人,必须有人出去他才能出去。用代码怎么实现呢?

import time
import random
from multiprocessing import Process
from multiprocessing import Semaphore

也可以简写,如import tinme,random这样  下面也可以那样简写。

def ktv(i,sem):
    sem.acquire()    #获取钥匙
    print('%s走进ktv'%i)
    time.sleep(random.randint(1,5))
    print('%s走出ktv'%i)
    sem.release()   #还钥匙


if __name__ == '__main__' :
    sem = Semaphore(4)  #总共钥匙总数为4
    for i in range(20):
        p = Process(target=ktv,args=(i,sem))
        p.start()

解读:总共 sem = Semaphore(4) 表示总共钥匙总数为4,然后把他进程实例化的参数,传到上面,上面才能用sem.acquire()拿钥匙这个和 sem.release() 表示还钥匙。

注意:sem = Semaphore() 什么都不写,默认一把钥匙。也就是一个人走进,一个出去才能下一个人,相当于买火车票上锁了。

2 什么是事件?为什么要引入事件。

当我们需要通过一个信号来控制多个进程阻塞或者执行就引入事件。

代码表示:from multiprocessing import Event

怎样创建事件?

e=Event()   当一个事件创建时,默认是阻塞状态。

怎么查看是否是阻塞状态呢?

print(e.is_set())  查看进程的状态,默认是阻塞状态。

怎么改变他状态呢?

e.set()  将事件改变为非阻塞,也就是True

e.clear() 将事件改变为阻塞,也就是False

e.wait()又是怎么用的呢?

与e.is_set()连用,当判断是阻塞状态时,e.wait()起到阻塞作用,后面的代码不执行。

也即是这样:

# set 和 clear
    #  分别用来修改一个事件的状态 True或者False
# is_set 用来查看一个事件的状态
# wait 是依据事件的状态来决定自己是否在wait处阻塞
    #  False阻塞 True不阻塞

例题:红绿灯

import time
import random
from multiprocessing import Event,Process
def cars(e,i):
    if not e.is_set():
        print('car%i在等待'%i)
        e.wait()    # 阻塞 直到得到一个 事件状态变成 True 的信号
    print('\033[0;32;40mcar%i通过\033[0m' % i)

def light(e):
    while True:
        if e.is_set():
            e.clear()
            print('\033[31m红灯亮了\033[0m')
        else:
            e.set()
            print('\033[32m绿灯亮了\033[0m')
        time.sleep(2)

if __name__ == '__main__':
    e = Event()
    traffic = Process(target=light,args=(e,))
    traffic.start()
    for i in range(20):
        car = Process(target=cars, args=(e,i))
        car.start()
        time.sleep(random.random())

解读:

首先理解,灯和车是同时执行的,两个进程相互不影响。

然后if e.is_set():

         e.clear()

      不执行红灯,而是执行绿灯,下一行也是这样。

打印结果:

绿灯亮了
car0通过
car1通过
car2通过
car3通过
红灯亮了
car4在等待
car5在等待
car6在等待
car7在等待
car8在等待
绿灯亮了
car4通过
car7通过
car8通过
car5通过
car6通过
car9通过
car10通过
car11通过
红灯亮了
car12在等待
car13在等待
绿灯亮了
car13通过
car12通过
car14通过
car15通过
car16通过

还在循环。

3 什么是队列?(先进先出)

实现多进程之间的通信,也叫 IPC(Inter-Process Communication) 。

创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递 。

Queue([maxsize]) 
创建共享的进程队列。
参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。
底层队列使用管道和锁定实现。

方法介绍:

Queue([maxsize]) 
创建共享的进程队列。maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。底层队列使用管道和锁定实现。
另外,还需要运行支持线程以便队列中的数据传输到底层管道中。 
Queue的实例q具有以下方法:

q.get( [ block [ ,timeout ] ] ) 
返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。block用于控制阻塞行为,默认为True. 
如果设置为False,将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。
如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。

q.get_nowait( ) 
同q.get(False)方法。

q.put(item [, block [,timeout ] ] ) 
将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。如果设置为False,
将引发Queue.Empty异常(定义在Queue库模块中)。timeout指定在阻塞模式中等待可用空间的时间长短。
超时后将引发Queue.Full异常。

q.qsize() 
返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,
队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。

例题2:

from multiprocessing import Queue,Process
def produce(q):
    q.put('hello') 把hello放入队列

def consume(q):
    print(q.get())  返回队列中的项   也就是获得hello

if __name__ == '__main__':
    q = Queue()
    p = Process(target=produce,args=(q,))
    p.start()
    c = Process(target=consume, args=(q,))
    c.start()

打印结果:hello

4 生产者模型:使用queue模块。

# 队列
# 生产者消费者模型

# 生产者 进程
# 消费者 进程
import time
import random
from multiprocessing import Process,Queue
def consumer(q,name):
    while True:
        food = q.get()
        if food is None:
            print('%s获取到了一个空'%name)
            break
        print('\033[31m%s消费了%s\033[0m' % (name,food))
        time.sleep(random.randint(1,3))

def producer(name,food,q):
    for i in range(4):
        time.sleep(random.randint(1,3))
        f = '%s生产了%s%s'%(name,food,i)
        print(f)
        q.put(f)

if __name__  == '__main__':
    q = Queue(20)
    p1 = Process(target=producer,args=('Egon','包子',q))
    p2 = Process(target=producer, args=('wusir','泔水', q))
    c1 = Process(target=consumer, args=(q,'alex'))
    c2 = Process(target=consumer, args=(q,'jinboss'))
    p1.start()
    p2.start()
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    q.put(None)
    q.put(None)

升级版本:模块JoinableQueue

import time
import random
from multiprocessing import Process,JoinableQueue   用的这个
def consumer(q,name):
    while True:
        food = q.get()
        print('\033[31m%s消费了%s\033[0m' % (name,food))
        time.sleep(random.randint(1,3))
        q.task_done()     # count - 1

def producer(name,food,q):
    for i in range(4):
        time.sleep(random.randint(1,3))
        f = '%s生产了%s%s'%(name,food,i)
        print(f)
        q.put(f)
    q.join()    # 阻塞  直到一个队列中的所有数据 全部被处理完毕

if __name__  == '__main__':
    q = JoinableQueue(20)
    p1 = Process(target=producer,args=('Egon','包子',q))
    p2 = Process(target=producer, args=('wusir','泔水', q))
    c1 = Process(target=consumer, args=(q,'alex'))
    c2 = Process(target=consumer, args=(q,'jinboss'))
    p1.start()
    p2.start()
    c1.daemon = True   # 设置为守护进程 主进程中的代码执行完毕之后,子进程自动结束
    c2.daemon = True
    c1.start()
    c2.start()
    p1.join()
    p2.join()      # 感知一个进程的结束

#  在消费者这一端:
    # 每次获取一个数据
    # 处理一个数据
    # 发送一个记号 : 标志一个数据被处理成功

# 在生产者这一端:
    # 每一次生产一个数据,
    # 且每一次生产的数据都放在队列中
    # 在队列中刻上一个记号
    # 当生产者全部生产完毕之后,
    # join信号 : 已经停止生产数据了
                # 且要等待之前被刻上的记号都被消费完
                # 当数据都被处理完时,join阻塞结束

# consumer 中把所有的任务消耗完
# producer 端 的 join感知到,停止阻塞
# 所有的producer进程结束
# 主进程中的p.join结束
# 主进程中代码结束
# 守护进程(消费者的进程)结束

猜你喜欢

转载自my.oschina.net/u/3657436/blog/1785765
今日推荐