并发编程(二) 进程、协程

三 Python进程

3.1 multiprocessing类对象

from multiprocessing import Process


def bar(i):
    print('hello NO.{0}'.format(i))


for i in range(10):
    p = Process(target=bar, args=(i,))
    p.start()

ps:由于进程之间的数据需要各自持有一份,所以创建进程需要的非常大的开销。

multiprocessing的特点:

  • Python中的多进程管理模块是multiprocessing模块。
  • 通过multiprocessing.Process来创建一个进程对象,Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。
  • multiprocessing与threading一样,调用同一套API。

3.2 进程间通讯

...

3.3 进程间数据共享

数据共享主要有两种方式,一种是共享内存,另一种是通过数据管理器(Manager)来实现。

3.3.1 共享内存

Value

from multiprocessing import Process, Lock, Value

Array

from multiprocessing.sharedctypes import Array
from multiprocessing import Process, Lock

3.3.2 数据管理器

Manager

from multiprocessing import Process, Lock, Manager

四 协程

与一个进程可以包含多个线程一样,一个程序可以包含多个协程,因而下面我们来比较协程和线程。

我们知道多个线程相对独立,有自己的上下文,切换受系统控制;
而协程也相对独立,有自己的上下文,但是其切换由自己控制,例如,A协程切换到B协程是由A协程来控制的。

协程原理:

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。
因此,协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

4.1 yield实现协程

协程的特点:

  • 协程是单线程,不能再切换
  • 不再有任何锁的概念

整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。

import time

def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('[CONSUMER] ←← Consuming %s...' % n)
        time.sleep(1)
        r = '200 OK'

def produce(c):
    next(c)
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] →→ Producing %s...' % n)
        cr = c.send(n)
        print('[PRODUCER] Consumer return: %s' % cr)

    c.close()

if __name__ == '__main__':
    c = consumer()
    produce(c)

原理:
生成器函数或者协程函数中的yield语句挂起函数的执行,直到稍后使用next()或send()操作进行恢复为止。

4.2 greenlet

greenlet机制的主要思想:
可以使用一个调度器循环在一组生成器函数之间协作多个任务。
greenlet是python中实现我们所谓的"Coroutine(协程)"的一个基础库.

特点:

  • 方便手动切换

调用switch()方法,进行切换

from greenlet import greenlet
 
def test1():
    print (12)
    gr2.switch()
    print (34)
    gr2.switch()
 
def test2():
    print (56)
    gr1.switch()
    print (78)
 
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

4.3 gevent

gevent是第三方库,通过greenlet实现协程。

原理:
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

特点:

  • 自动切换

监听IO,实现自动切换

import gevent
import time

def foo():
    print("running in foo")
    gevent.sleep(2)
    print("switch to foo again")

def bar():
    print("switch to bar")
    gevent.sleep(5)
    print("switch to bar again")

start=time.time()

gevent.joinall( #启动函数
    [gevent.spawn(foo),
    gevent.spawn(bar)]
)

print(time.time()-start)

ps:由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:

from gevent import monkey
monkey.patch_all()
import gevent
from urllib import request
import time

def f(url):
    print('GET: %s' % url)
    resp = request.urlopen(url) #urlopen去页面取出页面内容,起到IO作用
    data = resp.read()
    print('%d bytes received from %s.' % (len(data), url))

start=time.time()

gevent.joinall([
        gevent.spawn(f, 'https://itk.org/'),
        gevent.spawn(f, 'https://www.github.com/'),
        gevent.spawn(f, 'https://zhihu.com/'),
])

# f('https://itk.org/')
# f('https://www.github.com/')
# f('https://zhihu.com/')

print(time.time()-start)
posted @ 2017-05-09 16:19  六神酱  阅读(146)  评论(0编辑  收藏  举报