Python(进程,协程)

本文通过多个示例对比了Python中进程与线程的区别及应用,包括进程的基本使用方法、进程间通信方式如队列和管道等,以及进程池的使用场景。同时介绍了协程的概念及其优势。
#进程的调用方式和线程差不多,基本一样,但是进程用到了多核,是真正意义上的并发,下面的例子可以看出这个现象
from multiprocessing import Process
import time


def f(name):
    time.sleep(1)
    print('hello', name,time.ctime())

if __name__ == '__main__':
    p_list=[]
    for i in range(3):

        p = Process(target=f, args=('alvin',))
        p_list.append(p)
        p.start()

    for i in p_list:
        i.join()
    print('end')
import multiprocessing,time
def mul(m):
    mul = 1

    for i in range(1,m):
        mul*=i
    print(mul)
def mul1(m):
    mul = 1

    for i in range(1,m):
        mul*=i
    print(mul)

if __name__ == '__main__':
    t = time.time()
    p1 = multiprocessing.Process(target=mul,args=(100000,))
    p2 = multiprocessing.Process(target=mul1, args=(100000,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('end----------',time.time()-t)#27.518165826797485
    t = time.time()
    mul1(100000)
    mul(100000)
    print('end----------', time.time() - t)#54.77262306213379
    import threading
    tm = time.time()
    t1 = threading.Thread(target=mul,args=(100000,))
    t2 = threading.Thread(target=mul1, args=(100000,))
    l = []
    l.append(t1)
    l.append(t2)
    for t in l:
        t.start()
    for t in l:
        t.join()
    print('end----------', time.time() - tm)#54.54677987098694#
from multiprocessing import Process
import time
class MyProcess(Process):
    def __init__(self,name):
        super(MyProcess,self).__init__()
        self.name = name
    def run(self):
        time.sleep(1)
        print('helle %s'%self.name,time.ctime())
if __name__ == '__main__':
    l = []
    for i in range(5):
        l.append(MyProcess('%dalex'%i))
    for p in l:
        p.start()
    for p in l:
        p.join()
    print('ending----------')

构造方法:

Process([group [, target [, name [, args [, kwargs]]]]])

group: 线程组,目前还没有实现,库引用中提示必须是None;
  target: 要执行的方法;
  name: 进程名;
  args/kwargs: 要传入方法的参数。

实例方法:

is_alive():返回进程是否在运行。

join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。

start():进程准备就绪,等待CPU调度

run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。

terminate():不管任务是否完成,立即停止工作进程

属性:

daemon:和线程的setDeamon功能一样

name:进程名字。

pid:进程号。

import time
from  multiprocessing import Process

def foo(i):
    time.sleep(1)
    print (p.is_alive(),i,p.pid)
    time.sleep(1)

if __name__ == '__main__':
    p_list=[]
    for i in range(10):
        p = Process(target=foo, args=(i,))
        #p.daemon=True
        p_list.append(p)

    for p in p_list:
        p.start()
    # for p in p_list:
    #     p.join()

    print('main process end')

进程通信
进程队列Queue

import queue,multiprocessing,time
def foo(q):
    time.sleep(1)
    print('son.process',id(q))#son.process 52005168   #son.process 51108208两个子进程的ID并不一样
    q.put(123)
    q.put('hello')
if __name__ == '__main__':
    q = multiprocessing.Queue()
    p1 = multiprocessing.Process(target=foo,args=(q,))
    p2 = multiprocessing.Process(target=foo, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('main process',id(q))#main process 46033040
    print(q.get())
    print(q.get())

管道

from multiprocessing import Process, Pipe
    def f(conn):
        conn.send([12, {"name":"yuan"}, 'hello'])
        response=conn.recv()
        print("response",response)
        conn.close()
        print("q_ID2:",id(conn))

    if __name__ == '__main__':

        parent_conn, child_conn = Pipe() #双向管道

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

        print(parent_conn.recv())   # prints "[42, None, 'hello']"
        parent_conn.send("儿子你好!")
        p.join()

Managers
Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。

from multiprocessing import Process,Manager
def foo(d,l,n):
    d[n] = '1'
    d['2'] = 2
    d[3] = '3'
    l.append(n)
    print('son process',id(d),id(l))
if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict()
        l = manager.list(range(3))
        print('main process',id(d),id(l))#main process 47908880 47212976
        p_list = []
        for i in range(4):
            p = Process(target=foo, args=(d, l, 1))
            p.start()#son process 44165808 44153072  son process 58059440 58046704
            #son process 54848176 54835440    son process 46656176 46643440
            #四个子进程公享一个列表和字典
            p_list.append(p)
        for res in p_list:
            res.join()
        print(d)#{1: '1', '2': 2, 3: '3'}
        print(l)#[0, 1, 2, 1, 1, 1, 1]

进程同步

from multiprocessing import Process, Lock
import time

def f(l, i):

        #l.acquire()
        time.sleep(1)
        print('hello world %s' % i)
        #l.release()

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

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

#如果实在cmd终端上运行,不加锁的话打印时可能出现10个进程有的进程打印的东西在同一行上,原因是这十个进程公用屏幕的资源,哪个进程
#先抢到,哪个进程先打印,如果一样快的会就会共同使用屏幕资源,这是加锁的话就是屏幕这个资源同一时间只能运行一个进程,相当于串行

进程池

from  multiprocessing import Process,Pool
import time,os

def Foo(i):

    time.sleep(5)

    print(i,"son",os.getpid())#0 son 8956   1 son 7736 子进程id是一直变化的

    return "HELLO %s"%i


def Bar(arg):#这个arg就是之前函数的的返回值,就是 "HELLO %s"%i,这个是必需要填的
    #print(arg)
    # print("hello")
    print(arg,"Bar:",os.getpid())#HELLO 0 Bar: 972    HELLO 1 Bar: 972  id972始终是固定的


if __name__ == '__main__':

    pool = Pool(5)#这就是进程池,进程池最大进程数是5,如果不写就默认按着电脑的核数与运行
    print("main pid",os.getpid())#main pid 972 主进程ID是972
    for i in range(100):
        #pool.apply(func=Foo, args=(i,))  #同步接口
        #pool.apply_async(func=Foo, args=(i,))

        #回调函数:  就是某个动作或者函数执行成功后再去执行的函数,foo函数执行完之后会立即执行Bar函数,bar函数是在主进程下运行
        #如果把Bar函数放到foo下,那么这个逻辑是在子进程下运行

        pool.apply_async(func=Foo, args=(i,),callback=Bar)

    pool.close()
    pool.join()         # join与close调用顺序是固定的,这个是规定的格式

    print('end')
#举例100块砖,一个人最多搬一块儿,怎么搬
#如果找100个人,那么资源消耗太多,如果1个人去搬,那么时间太长
#我们开一个池子,就是进程池,就是限定一下里面最大的进程数设置为5,意思就是最大有5个人过来搬
#如果我们的电脑是4核,那么我们的进程运行有快有慢,这时先运行完的就结束了,此时进程池空出一个位置,这时候就能再进来一个进程
#为什么还是5个5个的出,是因为电脑运行很快,看不出来明显差距

协程

import time
import queue

def consumer(name):

    print("--->ready to eat baozi...")
    while True:
        new_baozi = yield #没有包子时,此时系统阻塞
        print("[%s] is eating baozi %s" % (name,new_baozi))
        #time.sleep(1)

def producer():

    r = con.__next__()#这个相当于consumer函数执行了一次
    r = con2.__next__()

    n = 0
    while 1:
        time.sleep(1)
        print("\033[32;1m[producer]\033[0m is making baozi %s and %s" %(n,n+1) )
        con.send(n)#这个地方相当于把包子给到了con即consumer的new_baozi拿到了这个值,并且继续往下走
        con2.send(n+1)
        n +=2


if __name__ == '__main__':

    con = consumer("c1")#这个不是执行函数,只是创建了两个生成器,没有__next__方法,consumer函数是不执行的
    con2 = consumer("c2")
    producer()
#上面这个例子已经是一个协程的提现了

Greenlet
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

from greenlet import  greenlet
def test1():
    print(12)
    gr2.switch()
    print(13)
    gr2.switch()
    print(14)
def test2():
    print(12)
    gr1.switch()
    print(13)
    gr1.switch()
    print(14)#这个没有被执行,因为test1函数打印14的后面没有gr2.switch()
gr2 = greenlet(test2)
gr1 = greenlet(test1)
gr1.switch()#switch就是切换执行函数

Gevent

import gevent
import requests,time
start=time.time()
def f(url):
    print('GET: %s' % url)
    resp =requests.get(url)
    data = resp.text
    end = time.time()
    print('%d bytes received from %s.' % (len(data), url),end-start)

# f('https://www.python.org/')
# f('https://www.yahoo.com/')
# f('https://www.baidu.com/')
# f('https://www.sina.com.cn/')
# f("http://www.xiaohuar.com/hua/")

# gevent.joinall([
#         gevent.spawn(f, 'https://www.python.org/'),#4.989989757537842
#         gevent.spawn(f, 'https://www.yahoo.com/'),#13.485978603363037
#         gevent.spawn(f, 'https://www.baidu.com/'),#13.558139324188232
#         gevent.spawn(f, 'https://www.sina.com.cn/')#13.657403945922852,最后一共用时13.65左右
# ])

f('https://www.python.org/')

f('https://www.yahoo.com/')

f('https://baidu.com/')

f('https://www.sina.com.cn/')#13.742876529693604这个最终用时13.74

#线程,进程争夺资源是无法控制的,哪个先拿到资源哪个先用
#协程,协作式:--------非抢占式的程序
#                        yield(协程的关键词)
#协程主要解决的也是IO操作的,协程本质上就是一个线程,所以中间的转换消耗就没有了
#优势:没有切换的消耗,没有锁的概念了
#有问题,能用多核吗?可以采用多进程+协程,一个很好的解决并发的方案
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值