Python并发编程 06 进程、协程

一、多进程调用

与多线程调用相似

from multiprocessing import Process
import time

def f(name):
    time.sleep(1)
    print("hello",name,time.strftime("%X"))

if __name__ == '__main__':
    print("start",time.strftime("%X"))
    p_list=[]
    for i in range(3):
        p = Process(target=f, args=("LuMX",))
        p_list.append(p)
        p.start()
    for i in p_list:
        i.join()
    print("end",time.strftime("%X"))
'''
start 15:09:57
hello LuMX 15:09:59
hello LuMX 15:09:59
hello LuMX 15:09:59
end 15:09:59
'''
from multiprocessing import Process
import time

class MyProcess(Process):
    def run(self):
        time.sleep(1)
        print("hello",self.name,time.strftime("%X"))

if __name__ == '__main__':
    print("start",time.strftime("%X"))

    for i in range(3):
        p=MyProcess()
        p.daemon=True
        p.start()

    print("end",time.strftime("%X"))
    
'''
start 15:26:31
end 15:26:31
'''
from multiprocessing import Process
import os, time


def info(title):
    print("title:", title)
    print("parent process:", os.getppid())  # 父进程的ID
    print("process id", os.getpid())

if __name__ == '__main__':
    info("main process line")

    time.sleep(1)
    print("----------------")
    p = Process(target=info,args=("LuMX",))
    p.start()
    p.join()
    
'''
title: main process line
parent process: 9660
process id 12900
----------------
title: LuMX
parent process: 12900
process id 14000
'''

二、Process类

1、主要参数

target:要执行的方法
name:进程名
args/kwargs:要传入方法的参数

2、实例方法

is_alive():返回进程是否在运行
join([timeout]):阻塞当前环境的进程,直到调用此方法的进程终止或到达指定的timeout。
start():进程准备就绪,等待CPU调度
run():start()调用run方法,如果实例化进程时未指定传入target,则start默认执行run方法
terminate():不管任务是否完成,立即停止工作进程。

3、属性

daemon:和线程的setDaemon功能一样
name:进程名字
pid:进程号

4、代码示例

import time
from multiprocessing import Process

class MyProcess(Process):
    def __init__(self,num,name):
        super(MyProcess,self).__init__(name=name)
        self.num=num

    def run(self):
        time.sleep(1)
        print(f"name:{self.name} pid:{self.pid} alive:{self.is_alive()} num:{self.num}",time.strftime("%X"))
        time.sleep(10)

if __name__ == '__main__':
    print("main process start",time.strftime("%X"))
    p_list=[]
    for i in range(4):
        p = MyProcess(i,f"L{i}")
        p_list.append(p)

    for p in p_list:
        p.start()

    for p in p_list:
        p.join(1)
    print("main process end",time.strftime("%X"))

三、进程通讯

1、进程队列通讯

import queue,time
import multiprocessing
def foo(q):
    time.sleep(1)
    print("son process",id(q))
    q.put(123)
    q.put("haha")

if __name__ == '__main__':
    q=multiprocessing.Queue() # 进程队列
    p=multiprocessing.Process(target=foo,args=(q,))
    p.start()
    print("main process",id(q))
    print(q.get())
    print(q.get())

'''
main process 2032743575504
son process 2174242762864
123
haha
'''

2、管道通讯

from multiprocessing import Process, Pipe
import time


def f(conn):
    time.sleep(3)
    conn.send([12, {"age": 18}, "hellow"])
    response=conn.recv()
    print("response",response)
    conn.close()
    print("ID2:",id(conn))

if __name__ == '__main__':
    parent_conn, child_conn = Pipe() # 返回两个双向管道

    print("ID1",id(child_conn))
    p=Process(target=f,args=(child_conn,))
    p.start()

    print(parent_conn.recv())
    time.sleep(2)
    parent_conn.send("hellow,Son")
    p.join()

3、Manager对象

进程队列和管道均是进程间通讯,即进程A的数据信息发送给进程B。
Manager对象支持进程数据共享,即进程A修改数据后,进程B访问该数据,是修改后的结果。
Manager对象支持的数据类型有:list, dict, Namespace, Lock, RLock, Semaphore, Queue等。

from multiprocessing import Process,Manager

def f(d,l,n):
    d[n] = "1"
    d["2"] = 2

    l.append(n)

if __name__ == '__main__':
    with Manager() as manager:
        d=manager.dict()    # 设置一个字典对象为共享数据
        l=manager.list()    # 设置一个列表对象为共享数据
        p_list=[]

        for i in range(5):
            p = Process(target=f,args=(d,l,i))
            p.start()
            p_list.append(p)

        for res in p_list:
            res.join()

        print(d)    # {1: '1', '2': 2, 0: '1', 2: '1', 3: '1', 4: '1'}
        print(l)    # [1, 0, 2, 3, 4]

四、进程同步

from multiprocessing import Process,Lock
import time

def f(l,i):
    l.acquire()
    time.sleep(1)
    print("hellow world %s" %i)
    l.release()

if __name__ == '__main__':
    lock = Lock()

    for num in range(5):
        Process(target=f, args=(lock,num)).start()

在上述例子中,各个进程并发申请向屏幕打印内容。如果不进行进程同步,可能上一个进程还没打印完,下一个进程就开始打印,造成窜行。

五、进程池

有50个任务,每个任务执行时间1s。如果开50个进程同时干,对系统资源的开销太大。而只开1个进程干,又太浪费时间。而进程池就是一个折中的方法。
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程。如果进程池序列中没有可供使用的进程,程序就会等待。
进程池中有两个方法:
aplly: 同步接口
apply_async: 异步接口

from multiprocessing import Process,Pool
import time,os

def Foo(i):
    time.sleep(1)
    print(i,time.strftime("%X"))
    print("son",os.getpid())

    return "hello %s" %i

def Bar(arg):
    print("arg:",arg)   # args为Foo返回值
    print("Bar:",os.getpid())

if __name__ == '__main__':
    pool = Pool(5)  # 进程池中开5个进程同时干活
    print("main pid",os.getpid())
    for i in range(50):
        pool.apply_async(func=Foo,args=(i,),callback=Bar)
        # func:表示要执行的函数。
        # args:表示向执行函数传递的参数
        # callback:表示回调函数,就是func函数执行成功后再去执行的函数,它在主进程里运行

    # 下面两句代码顺序是固定的
    pool.close()    # 进程池不再接受新的任务
    pool.join()     # 等待所有任务执行完成

    print("end")

六、协程

1、协程简述

协程又称微线程,是非抢占式的,底层实现是用yield语句切换函数。协程主要解决的也是IO操作密集型问题。协程实际上就是一个线程在执行,所以省去线程切换的开销,也不需要多线程的锁机制。
用多进程+协程的方法,既可以充分利用多核,又充分发挥协程的高效率。

import time


def consumer(name):
    print("-->ready to eat baozi...")
    while True:
        new_baozi = yield
        print("[%s] is eating baozi %s" % (name, new_baozi))


def producer():
    con.__next__()
    con2.__next__()

    n = 0
    while n<=10:
        time.sleep(1)
        print("producer is making baozi %s and %s" % (n, n + 1))
        con.send(n)
        con2.send(n + 1)
        n += 2
        print("-----------")


if __name__ == '__main__':
    con = consumer("c1")
    con2 = consumer("c2")
    producer()

2、用greenlet库实现协程

from greenlet import greenlet

def test1():
    print(12)
    gr2.switch()
    print(34)

def test2():
    print(56)
    gr1.switch()    # 切换到test1
    print(78)
    gr1.switch()

# 返回两个greenlet对象,不需要再提前声明为生成器函数
gr1 = greenlet(test1)
gr2 = greenlet(test2)

print(gr1)
gr2.switch()    # 切换到test2

'''
<greenlet.greenlet object at 0x000002417FDECC00 (otid=0x0000000000000000) pending>
56
12
78
34
'''

3、用gevent库实现协程

gevent库比greenlet库进一步简化操作,可以不用手动切换协程,IO阻塞时,自动完成切换。

import gevent
import requests,time

start = time.time()

def f(url):
    print("GET: %s" %url)
    resp = requests.get(url)    # 爬取网页数据
    data = resp.text
    print("%d bytes received from %s" %(len(data), url))

# joinall方法:等待一组Greenlet(协程)完成
gevent.joinall([
    # spawn方法作用是创建一个协程
    gevent.spawn(f, 'https://www.python.org/'),
    gevent.spawn(f, 'https://www.yahoo.com/'),
    gevent.spawn(f, 'https://www.baidu.com/'),
    gevent.spawn(f, 'https://www.sina.com.cn/'),
])

print("cost time:", time.time()-start)
  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值