• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
KK筑梦人
博客园    首页    新随笔    联系   管理    订阅  订阅

上节复习、进程同步控制(Lock/锁、Semaphore/信号量、Event/事件)、进程间通信(队列) 第三十二天 2018.11.21

上节复习:

多进程代码:

from multiprocessing import Process

方法

  进程对象.start() 开启一个子进程

  进程对象.join() 感知一个子进程的结束

  进程对象.terminate() 结束一个子进程

  进程对象.is_alive() 查看某个子进程是否还在运行

属性

  进程对象.name 进程名

  进程对象.pid 进程号

  进程对象.daemon 值为True的时候,表示新的子进程是一个守护进程

      守护进程 随着主进程代码的执行结束而结束

      一定在start之前设置

from multiprocessing import Lock

l = Lock()

l.acquire() # 拿钥匙

会造成数据不安全的操作

l.release() # 还钥匙

多进程的补充---->子进程使用input报错问题

# 报错---->子进程在另一个进程中开启,无法再主进程中干预另一个进程中的子进程
# 运行时input在主进程屏幕输入数据,无法传递到运行中的子进程

from multiprocessing import Process
def func():
    num = input('>>>')        # 此时运行input时在主进程内输入,开启的子进程无法接收到在主进程中输入的
    print(num)

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

进程同步控制---->Semaphore(信号量):

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。 

semaphore:是一个内置的计数器---->每当调用acquire()时,内置计数器-1;每当调用release()时,内置计数器+1

计数器不能小于0,当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

多进程中的组件:

  一套资源 同一时间 只能被n个人访问

  某一段代码 同一时间 只能被n个进程执行

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

sem = Semaphore(4)        # 只有4个acquire
sem.acquire()
print('拿到第一把钥匙')
sem.acquire()
print('拿到第二把钥匙')
sem.acquire()
print('拿到第三把钥匙')
sem.acquire()
print('拿到第四把钥匙')
sem.acquire()
print('拿到第五把钥匙')
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)
    for i in range(20):
        p = Process(target=ktv,args=(i,sem))
        p.start()

信号量---->Semaphore---->利用锁---->内部加了一个计数器

信号量和锁的区别---->锁每次只能同时进一个;信号量每次可以同时进入的个数自己控制

进程同步控制---->Event(事件):

通过一个信号 来控制 多个进程 同时 执行或者阻塞事件

通常与函数结合使用,函数不会在事件发生前被执行!

Event事件

from multiprocessing import Event   ---->  一个信号可以使所有的进程都进入阻塞状态;也可以控制所有的进程解除阻塞

一个事件被创建之后,默认是阻塞状态

e = Event()  # 创建了一个事件
print(e.is_set())   # 查看一个事件的状态,默认被设置成阻塞
e.set()      # 将这个事件的状态改为True
print(e.is_set())
e.wait()     # 是依据e.is_set()的值来决定是否阻塞的
print(123456)
e.clear()    # 将这个事件的状态改为False
print(e.is_set())
e.wait()     # 等待 事件的信号被变成True
print('*'*10)

set和clear:

  分别用来修改一个事件的状态set(建立)----> True或者clear(清除)---->False

is_set      用来查看一个事件的状态

wait         是依据事件的状态来决定自己是否在wait处阻塞

False阻塞 True不阻塞

红绿灯事件:(因为只有True和False两种状态,所以简易的红绿灯事件只使用2种灯)

# 红绿灯事件
import time               # 导入time模块,模拟红灯绿灯时间,和车辆到来时间
import random             # 导入random模块,模拟车辆到来时间的不规则
from multiprocessing import Event,Process      # 导入事件和进程
def cars(e,i):           # 只负责按照事件值执行,不修改事件值
    if not e.is_set():          # 如果not False---->True(红灯亮了)---->与下面保持一致
        print('\033[31mcar%i在等待\033[0m'%i)   # 控制台输出格式
        e.wait()    # 阻塞 直到得到一个 事件状态变成 True 的信号
    print('\033[32mcar%i通过\033[0m' % i)

def light(e):              # 同时执行
    while True:           # 红绿灯不会停止无限循环---->可以设置为守护进程强行停止
        if e.is_set():     # 查看事件的状态,默认为False阻塞
            e.clear()      # 将这个事件改为False
            print('\033[31m红灯亮了\033[0m')    #事件为真时间才执行红灯
        else:              # 第一次传入为False
            e.set()        # 将这个事件改为True
            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):        # 模拟来20辆车
        car = Process(target=cars, args=(e,i))  # 来车的进程 传入的参数为事件,以及来的车为那个进程的
        car.start()            # 开启一个来车的子进程---->会被for循环20次
        time.sleep(random.random())     # 模拟每两辆来车之间的时间间隔

进程间通信---->Queue(队列):

队列:先进先出

from multiprocessing import Queue,Process

Queue([maxsize])---->创建共享的进程队列。maxsize是队列中允许的最大项数。

q.put(item [, block [,timeout ] ] ) ---->将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。

                如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)

                timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。

q.get( [ block [ ,timeout ] ] )---->取队列中一个东西,与put方法类似

'''
队列put放入东西,get取出东西
from multiprocessing import Queue
q = Queue(5)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
q.put(5)
print(q.full())   # 队列是否满了
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.empty())
用while取值
while True:
    try:
        q.get_nowait()    # 没收到取值会报错
    except:
        print('队列已空')
        time.sleep(0.5)
用for取值
for i in range(6):
    q.put(i)
'''
from multiprocessing import Queue,Process
def produce(q):
    q.put('hello')

def consume(q):
    print(q.get())

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

  

'''
multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的,但是队列接口
'''

from multiprocessing import Queue
q=Queue(3)

#put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
# q.put(3)   # 如果队列已经满了,程序就会停在这里,等待数据被别人取走,再将数据放入队列。
           # 如果队列中的数据一直不被取走,程序就会永远停在这里。
try:
    q.put_nowait(3) # 可以使用put_nowait,如果队列满了不会阻塞,但是会因为队列满了而报错。
except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去,但是会丢掉这个消息。
    print('队列已经满了')

# 因此,我们再放入数据之前,可以先看一下队列的状态,如果已经满了,就不继续put了。
print(q.full()) #满了

print(q.get())
print(q.get())
print(q.get())
# print(q.get()) # 同put方法一样,如果队列已经空了,那么继续取就会出现阻塞。
try:
    q.get_nowait(3) # 可以使用get_nowait,如果队列满了不会阻塞,但是会因为没取到值而报错。
except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去。
    print('队列已经空了')

print(q.empty()) #空了

单看队列用法

队列引入生产者、消费者模型:  

生产者消费者模型:

# 生产者 进程
# 消费者 进程
import time
import random
from multiprocessing import Process,Queue
def consumer(q,name):
    while True:
        food = q.get()                     # 在队列里取出东西,每次取出都为队列的第一个元素
        if food is None:                  # 如果队列里取出的东西为None则停止循环结束子进程
            print('%s获取到了一个空'%name)
            break
        print('\033[31m%s消费了%s\033[0m' % (name,food))   # 谁消费了f
        time.sleep(random.randint(1,3))

def producer(name,food,q):
    for i in range(10):
        time.sleep(random.randint(1,3))
        f = '%s生产了%s%s'%(name,food,i)   # 谁生产了什么东西 生产的是第几个
        print(f)
        q.put(f)                            # 把生产的对象放入队列里

if __name__  == '__main__':
    q = Queue(20)      # 最多存放20个
    # 每个生产者和消费者都是相互独立的,但是整体合起来需要受到Queue(20)控制
    p1 = Process(target=producer,args=('Egon','包子',q))      # 两个生产者最多生产20个
    p2 = Process(target=producer, args=('wusir','大饼', q))
    c1 = Process(target=consumer, args=(q,'alex'))            # 两个消费者最多消费20个
    c2 = Process(target=consumer, args=(q,'jinboss'))
    p1.start()
    p2.start()
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    # 两个消费者进程取同一队列,不一定两者取得数量一致
    q.put(None)            # 当生产者生产完成后,将2个消费者对应的2个结束的None放入队列最后
    q.put(None)

生产者消费者模型完善:  

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))    # 谁消费了f
        time.sleep(random.randint(1,3))
        q.task_done()     # count - 1  向q.join()发送一次信号,证明一个数据已经被取走了

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)          # 最多存放20个
    # 每个生产者和消费者都是相互独立的,但是整体合起来需要受到Queue(20)控制
    p1 = Process(target=producer,args=('Egon','包子',q))        # 两个生产者最多生产20个
    p2 = Process(target=producer, args=('wusir','大饼', q))
    c1 = Process(target=consumer, args=(q,'alex'))              # 两个消费者最多消费20个
    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阻塞结束

  

posted @ 2018-11-21 21:29  KK筑梦人  阅读(142)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3