博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

二、Python开发---25、多线程多进程(2)

Posted on 2020-01-12 09:43  兰智杰  阅读(193)  评论(0编辑  收藏  举报

多进程的几种方法

  Lock:可以避免访问资源时的冲突

  Pool:可以提供指定数量的进程

  Queue:多进程安全的队列,实现多进程之间的数据传递

  Pipe:实现管道模式下的消息发送与接收

Lock(加锁)

       同步执行:一个进程在执行任务时,另一个进程必须等待执行完毕,才能继续执行,加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改;没错,速度是慢了,但牺牲了速度却保证了数据安全

  异步执行:一个进程在执行任务时,另一个进程无需等待其执行完毕就可以执行,当有消息返回时,系统会提醒后者进行处理,这样会很好的提高运行效率

#加锁
'''
输出为  work_1 start
        work_2 start
        work_1 end
        work_2 end
'''
import time
import multiprocessing
def work_1(f,n,lock):
    print('work_1 start')
    lock.acquire()
    for i in range(n):
        with open(f,'a') as fs:
            fs.write('i love pyhton \n')
            time.sleep(1)
    print('work_1 end')
    lock.release()

def work_2(f,n,lock):
    print('work_2 start')
    lock.acquire()
    for i in range(n):
        with open(f,'a') as fs:
            fs.write('come on baby \n')
            time.sleep(1)
    print('work_2 end')
    lock.release()

if __name__ == '__main__':
    lock=multiprocessing.Lock()
    p1 = multiprocessing.Process(target=work_1,args = ('file.txt',3,lock))
    p2 = multiprocessing.Process(target=work_2, args=('file.txt', 1,lock))
    p1.start()
    p2.start()

Pool

  Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行它

  进程池方法: 

    1、apply(func[, args[, kwds]]):是阻塞的执行,在一个池工作进程中执行func(*args,**kwargs),然后返回结果;比如创建一个有3个进程的进程池,当执行时,是创建完一个,执行完函数再创建另一个,变成一个线性的执行

    2、apply_async(func[, args[, kwds[, callback]]]): 是非阻塞的执行,在一个池工作进程中执行func(*args,**kwargs),然后返回结果;比如创建一个3个进程的进程池,同时执行,只要有一个执行完立刻放回池子待下一个执行,并行的执行

    3、close(): 关闭pool,使其不再接受新的任务

    4、terminate(): 结束工作进程,不再处理未完成的任务 

    5、join(): 主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用

    例:apply_async()三个进程同时执行

'''
输出为 Parent process 5892 run work (0) ,work id 7696 run work (1) ,work id 5696 run work (2) ,work id 7656 work (0) stop ,work id 7696 run work (3) ,work id 7696 work (1) stop ,work id 5696 run work (4) ,work id 5696 work (2) stop ,work id 7656 work (3) stop ,work id 7696 work (4) stop ,work id 5696 ''' import os import multiprocessing import time def work(n): print('run work (%s) ,work id %s'%(n,os.getpid())) time.sleep(3) print('work (%s) stop ,work id %s'%(n,os.getpid())) if __name__=='__main__': print('Parent process %s' % os.getpid()) #获得当前进程的ID #创建进程池 p = multiprocessing.Pool(3) #进程池的容量为3 for i in range(5): #创建5个进程,一次进入进程池(进满) p.apply_async(work, args=(i,)) p.close() p.join()

    例:apply_async()两个进程同时执行

'''
输出为   Sun Jan 12 14:40:14 2020
        Sun Jan 12 14:40:14 2020
        您现在正在听的音乐是鸡你太美
        您现在正在看的电影是中国合伙人
        您现在正在听的音乐是鸡你太美
        Sun Jan 12 14:40:16 2020
        您现在正在看的电影是中国合伙人
        您现在正在听的音乐是大碗宽面
        结束时间是Sun Jan 12 14:40:17 2020
'''
import os
import multiprocessing
import time
def music(name,loop):
    print(time.ctime())
    for i in range(loop):
        time.sleep(1)
        print('您现在正在听的音乐是%s'%name)

def movie(name,loop):
    print(time.ctime())
    for i in range(loop):
        time.sleep(1)
        print('您现在正在看的电影是%s'%name)

if __name__=='__main__':
    pool=multiprocessing.Pool(2)
    pool.apply_async(func=music,args=('鸡你太美',2))
    pool.apply_async(func=movie,args=('中国合伙人',2))
    pool.apply_async(func=music, args=('大碗宽面', 1))
    pool.close()
    #pool.terminate()
    # 比较危险,不要轻易用,直接杀死进程池
    #join阻塞主进程,当子进程执行完毕的时候会继续往后执行,使用join必须在进程池使用terminate或者close之后
    pool.join()
    print('结束时间是%s'%time.ctime())

    例:apply()三个进程按顺序线性的执行

'''
输出为   Parent process 7568
        run work (0) ,work id 5052
        work (0) stop ,work id 5052
        run work (1) ,work id 5900
        work (1) stop ,work id 5900
        run work (2) ,work id 5148
        work (2) stop ,work id 5148
        run work (3) ,work id 5052
        work (3) stop ,work id 5052
        run work (4) ,work id 5900
        work (4) stop ,work id 5900
'''
import os
import multiprocessing
import time
def  work(n):
   print('run work (%s) ,work id %s'%(n,os.getpid()))
   time.sleep(3)
   print('work (%s) stop ,work id %s'%(n,os.getpid()))

if __name__=='__main__':
   print('Parent process %s' % os.getpid())     #获得当前进程的ID
   #创建进程池
   p = multiprocessing.Pool(3)                   #进程池的容量为3
   for i in range(5):
        #创建5个进程,一次进入进程池(进满)
        p.apply(work, args=(i,))
   p.close()
   p.join()

Queue

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

  创建队列的类(底层就是以管道和锁定的方式实现):

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

    参数介绍:maxsize是队列中允许最大项数,省略则无大小限制

  2、q.put()方法用以插入数据到队列中,put方法还有两个可选参数:blockedtimeout

    如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间;如果超时,会抛出Queue.Full异常;如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。

  3、q.get()方法可以从队列读取并且删除一个元素;

    同样,get方法有两个可选参数:blockedtimeout;如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常;如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常

  4、q.get_nowait():同q.get(False)

  5、q.put_nowait():同q.put(False)

  6、q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目

  7、q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走

  8、q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同上

'''
输出为  发送 A 到 queue...
        从 queue 接受 A
        发送 B 到 queue...
        从 queue 接受 B
        发送 C 到 queue...
        从 queue 接受 C
'''
import multiprocessing
import time
#queue 跨进程通信
def put(q):
   for value in ['A', 'B', 'C']:
       print('发送 %s 到 queue...' % value)
       q.put(value)  # 通过put发送
       time.sleep(1)
# 读数据进程执行的代码:
def get(q):
   while True:
       value = q.get(True) #接受队列中的数据
       print('从 queue 接受 %s' % value)

if __name__=='__main__':
    # 父进程创建Queue,并传给各个子进程:
    q = multiprocessing.Queue()
    pw = multiprocessing.Process(target=put, args=(q,))
    pr = multiprocessing.Process(target=get, args=(q,))
    # 启动子进程pw,写入:
    pw.start()
    # 启动子进程pr,读取:
    pr.start()
    # 等待pw结束:
    pw.join()
    # pr进程里是死循环,无法等待其结束,只能强行终止:
    pr.terminate()

Pipe

  Pipe方法返回(conn1,conn2)代表一个管道的两个端;Pipe方法有duplex参数:duplex 为 True(默认值),那么这个管道是全双工模式,也就是说conn1和conn2均可收发;duplex 为 False,conn1只负责接受消息,conn2只负责发送消息

  sendrecv方法分别是发送和接收消息的方法;在全双工模式下,可以调用conn1.send发送消息,conn1.recv接收消息;如果没有消息可接收,recv方法会一直阻塞;如果管道已经被关闭,那么recv方法会抛出EOFError

'''
输出为   发送 A 到 pipe...
        从 pipe 接受 A 
        发送 B 到 pipe...
        从 pipe 接受 B 
        发送 C 到 pipe...
        从 pipe 接受 C 
'''
import multiprocessing
import time
#PIPE 管道通信
def put(p):
    for value in ['A', 'B', 'C']:
        print ('发送 %s 到 pipe...' % value)
        p[1].send(value)                    #p[1]相当于conn2
        time.sleep(2)

# 读数据进程执行的代码:
def get(p):
    while True:
        value = p[0].recv()                 #p[0]相当于conn1
        print ('从 pipe 接受 %s ' % value)

if __name__=='__main__':
    # 父进程创建Pipe,并传给各个子进程:
    # p = multiprocessing.Pipe()
    p = multiprocessing.Pipe(duplex=False) #左收右发
    pw = multiprocessing.Process(target=put, args=(p,))
    pr = multiprocessing.Process(target=get, args=(p,))
    # 启动子进程pw,写入:
    pw.start()
    # 启动子进程pr,读取:
    pr.start()
    # 等待pw结束:
    pw.join()
    # pr进程里是死循环,无法等待其结束,只能强行终止:
    pr.terminate()

生产者消费者模型

  1、queue.Queue是进程内非阻塞队列(说白了就是线程)

  2、multiprocess.Queue是跨进程通信队列

'''
输出为  生产了骨头 1
        [King]取到[骨头1]并且吃了它...
        生产了骨头 2
        [Wang]取到[骨头2]并且吃了它...
        生产了骨头 3
        [King]取到[骨头3]并且吃了它...
        生产了骨头 4
        [Wang]取到[骨头4]并且吃了它...
        。。。
'''
#生产者消费者模型
import threading
import time
import queue
q = queue.Queue(maxsize=10)
def producer(name):  # 生产者
    count = 1
    while True:
        print("生产了骨头", count)
        q.put("骨头%s" % count)
        count += 1
        time.sleep(1)
def consumer(name):  # 消费者
    while True:
        print("[%s]取到[%s]并且吃了它..." % (name, q.get()))
        time.sleep(1)

p = threading.Thread(target=producer, args=("Tim",))
c1 = threading.Thread(target=consumer, args=("King",))
c2 = threading.Thread(target=consumer, args=("Wang",))
p.start()
c1.start()
c2.start()