python 里面的事件编程

记得第一次接触python的协程是在看scrapy的代码的时候,很久远的事情了。后来又断断续续的的接触了一些乱七八糟关于协程的文章代码,最后接触了golang,觉得golang语言级别支持协程超级赞,然后看了很长时间的golang的runtime的原理,以及对应的调度机制,虽然不敢说对于协程有很清晰的了解,但是对于协程这个概念也可以回答一些入门级的问题。

其实想想,抛开协程和线程的区别,抛开操作系统的调度以及语言层面的任务调度,协程和线程有神马区别吗?感觉好像确实没什么区别,作为一个程序员,应该理解以及掌握的无非就是线程/协程之间的通讯,消息队列这方面的事情。如果把协程比作一个个计算机的单个节点,那么应用了协程的进程不就是一个分布式吗?那么协程的思想和分布式的思想从本质上的区别又是什么呢?水平有限, 无法给予解答。

记得前两天被一个人问我python事件编程的东西,当时我是懵逼的,因为不知道这个词是什么意思,然后果断被鄙视了。然后回去查了下,选了个gevent库学习了下,下面是参照golang的的学习笔记, 记录一些入门的问题。

1: 创建并运行协程

  golang里面: go func(interface {})

  python 的 gevent: gevent.swap(func(*args, **kwargs)).join()

  由于go 对协程的语言层面的支持, 这里显得go会简单一点, python用gevent包的话需要打一个猴子补丁, 将对应的标准库来进行替换, 比如替换socket库的话:

from gevent import monkey
monkey.patch_socket()
 

  当然最简单的还是:

from gevent import monkey
monkey.patch_all()
patch_all()实际上是将I0用事件循环来处理了,从而避免了任务因io的block而被block。
2: 协程间的通信
  (a):消息队列的模式
    golang: channel
      生产消息 >- channel
      消费消息 <- channel, 好吧, 这里叫消费消息并不严谨, 这里实际上会跟select 一起使用, 监听channel。
    python: from gevent.queue import Queue
      生产消息: put 以及 putnowait
      消费消息: get 以及 getnowait
      下面是一个有3个生产者, 20个消费者的栗子
import gevent
from gevent import monkey
monkey.patch_all()
import socket
import time
import datetime
import random
    
from gevent.queue import Queue
    
q1 = Queue()
    
def do_produce():
    while True:
        time.sleep(1)
        # time.sleep(random.randint(1, 5))
        print "produce:", datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        q1.put(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
    
def _produce():
    gevent.spawn(do_produce).join()
    
def producer():
    return [gevent.spawn(_produce) for i in xrange(3)]
    
def  _consumer():
    while True:
        x = q1.get()
        print "consumed:", x
    
def consumer():
    return [gevent.spawn(_consumer) for i in xrange(20)]
jobs = producer().extend(consumer())
gevent.joinall(jobs)
输出为:

produce: 2017-12-18 17:41:06
produce: 2017-12-18 17:41:06
produce: 2017-12-18 17:41:06
consumed: 2017-12-18 17:41:06
consumed: 2017-12-18 17:41:06
consumed: 2017-12-18 17:41:06
produce: 2017-12-18 17:41:07
produce: 2017-12-18 17:41:07
produce: 2017-12-18 17:41:07
consumed: 2017-12-18 17:41:07
consumed: 2017-12-18 17:41:07
consumed: 2017-12-18 17:41:07
produce: 2017-12-18 17:41:08
produce: 2017-12-18 17:41:08
produce: 2017-12-18 17:41:08
consumed: 2017-12-18 17:41:08
consumed: 2017-12-18 17:41:08
consumed: 2017-12-18 17:41:08


  (b): 唤醒
    golang没有直接支持
    gevent直接集成该模式.gevent实现唤醒有两种方式, 第一种是AsyncResult, 能用来传递所发布的消息, 第二种是Event, 不能传递消息。     
import gevent
from gevent.event import Event
from gevent.event import AsyncResult
aevt = AsyncResult()  #异步pub/sub模式
 
def pub():
    print 'Wait for message pub'
    gevent.sleep(5)
    print "Ok, pub message"
    aevt.set('Hello!')
 
def sub():
    print("waiting for message")
    message = aevt.get()
    print 'Got message: %s' % message
 
gevent.joinall([
    gevent.spawn(pub),
    gevent.spawn(sub),
    gevent.spawn(sub),
    gevent.spawn(sub),
])

的输出为:  

  Wait for message pub
  waiting for message
  waiting for message
  waiting for message
  Ok, pub message
  Got message: Hello!
  Got message: Hello!
  Got message: Hello!

下面是用Event的实现的: 

import gevent
from gevent.event import Event
 
evt = Event()
 
def pub():
    print 'Wait for message pub'
    gevent.sleep(5)
    print "Ok, pub message"
    evt.set()
    # evt.set("hello")
 
def sub():
    message = evt.wait()
    print("waiting for message")
    print 'Finish waiting', message
 
gevent.joinall([
    gevent.spawn(pub),
    gevent.spawn(sub),
    gevent.spawn(sub),
    gevent.spawn(sub),
])

的输出为:

  Wait for message pub
  Ok, pub message
  waiting for message
  Finish waiting True
  waiting for message
  Finish waiting True
  waiting for message
  Finish waiting True    

  3: 协程之间的信号量
   这里golang由于对语言层面的协程支持的特别好,创建一个协程非常轻量,并没有做特殊的限制。python 里面用信号量来控制最多能创建的协程个数:
import gevent
from gevent.coros import BoundedSemaphore 
sem = BoundedSemaphore(3) #这里最多只能创建3个协程。
def worker(n):
    sem.acquire()
    sem.release()
gevent.joinall([gevent.spawn(worker, i) for i in xrange(0, 6)])

 

  
 
 

 

posted @ 2017-12-18 17:32  天使的魅影  阅读(1883)  评论(0编辑  收藏  举报