多进程

同步和异步

同步是指:当程序1调用程序2时,程序1停下不动,直到程序2完成回到程序1来,程序1才继续执行下去。  
异步是指:当程序1调用程序2时,程序1径自继续自己的下一个动作,不受程序2的的影响。

同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。  
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。

 

创建子进程

from multiprocessing import Process
import time
import os


def func(arg1, arg2):
    print('*'*arg1)
    time.sleep(2)
    print('子进程:', os.getpid())
    print('*'*arg2)


if __name__ == '__main__':  # windows系统中必须有这一行
    p = Process(target=func, args=(10, 20))  # 注册,args是传递参数的方式
    # p.daemon = True   # 设置子进程为守护进程,父进程结束后子进程自动结束
    p.start()  # 通知操作系统开启一个子进程
    print('########')
    p.join()    # 感知一个子进程的结束,将一个异步程序改为同步,join后的内容在p结束后才执行
    print('父进程:', os.getpid())
    print('父进程的父进程:', os.getppid())
    print('运行完了')

 

创建多进程

方法一

from multiprocessing import Process
import time


def func(arg):
    print('进程%s开始' % arg)
    time.sleep(1)
    print('进程%s结束' % arg)


if __name__ == '__main__':
    p_list = []
    for i in range(5):
        p = Process(target=func, args=(i,))
        p.start()
        p_list.append(p)
    for p in p_list:  # 所有子进程结束后再执行后面的父进程
        p.join()
    print('主进程结束')

方法二

import os
from multiprocessing import Process


class MyProcess(Process):
    def __init__(self, arg1, arg2):  # 传递参数的方式
        super().__init__()
        self.arg1 = arg1
        self.arg2 = arg2

    def run(self):  # 方法名必须为run
        print(os.getpid())
        print(self.arg1)
        print(self.arg2)


if __name__ == '__main__':
    print('主进程:', os.getpid())
    p1 = MyProcess(1, 2)
    p1.start()
    p2 = MyProcess(3, 4)
    p2.start()

 

守护进程

from multiprocessing import Process
import time
# 守护进程会等主进程的代码执行完之后自动结束,(代码执行完即可,不一定等到主进程结束)


def func():
    while True:
        time.sleep(0.5)
        print('子进程')


if __name__ == '__main__':
    p = Process(target=func)
    p.daemon = True  # 在p.start()之前,设置子进程为守护进程
    p.start()
    # print(p.name, p.pid)  # 进程名和进程id
    # print(p.is_alive())  # 检验进程是否还活着
    # p.terminate()  # 结束子进程

    i = 0
    while i < 5:
        print('主进程执行中')
        time.sleep(1)
        i += 1

 

进程锁

# 文件db的内容为:{"count":5}
# 注意一定要用双引号,不然json无法识别
# 并发运行,效率高,但竞争写同一文件,数据写入错乱
from multiprocessing import Process, Lock
import time,json,random


def search():
    dic = json.load(open('db'))
    print('\033[34m剩余票数%s\033[0m' % dic['count'])


def get(arg):
    dic = json.load(open('db'))
    time.sleep(0.2)  # 模拟读数据的网络延迟
    if dic['count'] > 0:
        dic['count'] -= 1
        time.sleep(0.2)  # 模拟写数据的网络延迟
        json.dump(dic, open('db', 'w'))
        print('进程%s' % arg + '\033[32m购票成功\033[0m')
    else:
        print('进程%s' % arg + '\033[31m购票失败\033[0m')


def task(lock, arg):
    search()
    lock.acquire()  # 拿钥匙
    get(arg)
    lock.release()  # 还钥匙


if __name__ == '__main__':
    lock = Lock()
    for i in range(10):  # 模拟并发10个客户端抢票
        p = Process(target=task, args=(lock, i))
        p.start()

 

信号量

from multiprocessing import Process, Semaphore
import time


def func(i, sem):
    sem.acquire()   # 拿钥匙
    print('进程%s开始执行' % i)
    time.sleep(5)
    print('进程%s执行结束' % i)
    sem.release()   # 还钥匙


if __name__ == '__main__':
    sem = Semaphore(4)  # 设置4把钥匙
    for i in range(20):
        p = Process(target=func, args=(i, sem))
        p.start()

 

事件

from multiprocessing import Event

e = Event()    # 创建了一个事件,默认状态是False
print(e.is_set())    # 查看事件状态
e.set()    # 将事件状态改为True
print(e.is_set())
e.wait()    # 根据事件状态决定是否阻塞,True不阻塞,False阻塞,不wait不阻塞。
print('*'*10)
e.clear()   # 将事件状态改为False
print(e.is_set())
e.wait()    # 等待时间状态改为True
print('*'*10)

红绿灯实现

from multiprocessing import Event, Process
import time
import random


def cars(e, i):
    if not e.is_set():
        print('car%s等待中' % i)
        e.wait()    # 阻塞,等待时间状态变为True
    print('car%s通过' % 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(1)


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())

 

进程池

简单实现

import os
import time
from multiprocessing import Pool


def func(n):
    print("start func%s" % n, os.getpid())
    time.sleep(1)
    print("end func%s" % n, os.getpid())


if __name__ == '__main__':
    p = Pool(5)
    for i in range(10):
        # p.apply(func, args=(i,))      # 同步执行
        p.apply_async(func, args=(i,))  # 异步执行
    p.close()   # 结束进程池接受任务
    p.join()    # 感知进程池中的任务执行结束

有返回值的进程池函数

import time
from multiprocessing import Pool
# 有返回值的进程池函数


def func(i):
    time.sleep(0.5)
    return i*i


if __name__ == '__main__':
    p = Pool(5)
    res_l = []
    for i in range(10):
        res = p.apply_async(func, args=(i,))
        res_l.append(res)
    for res in res_l:
        print(res.get())    # get方法获取res的返回值,不能放在上一个for里面,否则会改异步为同步

进程池回调函数

import os
from multiprocessing import Pool


def func1(n):
    print('func1:', os.getpid())
    return n*n


def func2(n):
    print('func2:', os.getpid())    # 回调函数在主进程中执行
    print(n)


if __name__ == '__main__':
    print('主进程:', os.getpid())
    p = Pool(5)
    p.apply_async(func1, args=(10,), callback=func2)    # func1的返回值作为参数传给回调函数
    p.close()
    p.join()

 

进程间通信

python提供了多种进程通信的方式,主要Queue和Pipe这两种方式,Queue用于多个进程间实现通信,Pipe是两个进程的通信

Queue有两个方法:

  • Put方法:以插入数据到队列中,他还有两个可选参数:blocked和timeout

  • Get方法:从队列读取并且删除一个元素。同样,他还有两个可选参数:blocked和timeout

from multiprocessing import Process, Queue
import os,time,random


# 写数据进程执行的代码
def proc_write(q, urls):
    print('Process is write....')
    for url in urls:
        q.put(url)
        print('put %s to queue... ' % url)
        time.sleep(random.random())


# 读数据进程的代码
def proc_read(q):
    print('Process is reading...')
    while True:
        url = q.get(True)
        print('Get %s from queue' %url)


if __name__ == '__main__':
    # 父进程创建Queue,并传给各个子进程
    q = Queue()
    proc_write1 = Process(target=proc_write, args=(q, ['url_1', 'url_2', 'url_3']))
    proc_write2 = Process(target=proc_write, args=(q, ['url_4', 'url_5', 'url_6']))
    proc_reader = Process(target=proc_read, args=(q,))
    # 启动子进程,写入
    proc_write1.start()
    proc_write2.start()

    proc_reader.start()
    # 等待proc_write1结束
    proc_write1.join()
    proc_write2.join()
    # proc_reader进程是死循环,强制结束
    proc_reader.terminate()

Pipe通信机制,

  Pipe常用于两个进程,两个进程分别位于管道的两端

  Pipe方法返回(conn1,conn2)代表一个管道的两个端,Pipe方法有duplex参数,默认为True,即全双工模式,若为FALSE,conn1只负责接收信息,conn2负责发送,

  send和recv方法分别为发送和接收信息

import multiprocessing
import os,time,random

#写数据进程执行的代码
def proc_send(pipe,urls):
    #print 'Process is write....'
    for url in urls:

        print 'Process is send :%s' %url
        pipe.send(url)
        time.sleep(random.random())

#读数据进程的代码
def proc_recv(pipe):
    while True:
        print('Process rev:%s' %pipe.recv())
        time.sleep(random.random())

if __name__ == '__main__':
    #父进程创建pipe,并传给各个子进程
    pipe = multiprocessing.Pipe()
    p1 = multiprocessing.Process(target=proc_send,args=(pipe[0],['url_'+str(i) for i in range(10) ]))
    p2 = multiprocessing.Process(target=proc_recv,args=(pipe[1],))
    #启动子进程,写入
    p1.start()
    p2.start()

    p1.join()
    p2.terminate()

 

posted @ 2019-10-19 16:12  tianqibucuo  阅读(110)  评论(0)    收藏  举报