[关闭]
@chyoo1991 2015-09-21T15:41:13.000000Z 字数 2242 阅读 3540

Python的多进程、多线程、协程

python coding


从某种角度看,进程与线程的形式很像,至少在Python里面, threading.Thread类与multiprocess.Process的方法还是挺像的。有些东西,往深入了理解,就能发现很多细节上的不同。

  1. import urllib2
  2. import time
  3. from threading import Thread
  4. URLs = ["http://www.baidu.com",
  5. "https://www.taobao.com/",
  6. "http://weibo.com/hi/",
  7. "http://www.jd.com/"
  8. ...] # 在大量访问url的时候,多线程很有优势,实际上也是空间换时间
  9. def normal_way():
  10. start = time.time()
  11. for url in URLs:
  12. urllib2.urlopen(url)
  13. print time.time() - start
  14. class URL(Thread):
  15. """
  16. 定义多线程访问url的类
  17. """
  18. def __init__(self, url):
  19. self.url = url
  20. super(URL, self).__init__()
  21. def run(self):
  22. urllib2.urlopen(self.url)
  23. def multi_process_way():
  24. num = len(URLs)
  25. threads = []
  26. start = time.time()
  27. for i in xrange(num):
  28. t = URL(URLs[i])
  29. threads.append(t)
  30. t.start()
  31. for i in xrange(num):
  32. threads[i].join() # 等线程执行完毕再执行主线程,保证时间上的统计是没问题的
  33. print time.time() - start
  34. if __name__ == '__main__':
  35. normal_way()
  36. multi_process_way()

经过测算,上述代码,在70个URL的时候,返回结果是:

  1. 6.29869413376 # 第一种单线程方式 单位是秒
  2. 0.443621873856 # 第二种多线程方式

由此可见,多线程在处理I/O的时候非常有优势,即使只用了一个核心。

但是多线程本身也有问题。除了GIL这个东西之外,当线程多的时候,线程的切换是一件十分耗时的工作。但是python里面提供了一个比较好玩的东西:协程,来解决这个问题。

协程是一个很特别的东西,其执行函数之间的切换,由函数自身控制。其本身的执行,是在单一线程中。可以设置不同的函数,执行不同的任务,函数之间的切换,通过yield实现。一个生产者与消费者的实例:

  1. import time
  2. def consumer():
  3. r = ''
  4. while True:
  5. n = yield r # 通过yield获得n的值,从另一个函数里面传进来,传出去r的值
  6. if not n:
  7. return
  8. print('[CONSUMER] Consuming %s...' % n)
  9. time.sleep(1)
  10. r = '200 OK'
  11. def produce(c):
  12. c.next() # c 是生成器对象(含有yield的函数就是生成器), 此句意思是启动生成器
  13. n = 0
  14. while n < 5:
  15. n = n + 1
  16. print('[PRODUCER] Producing %s...' % n)
  17. r = c.send(n) # 发送n 到生成器c,并取得生成器执行的结果的值,即r值
  18. print('[PRODUCER] Consumer return: %s' % r)
  19. c.close() # 关闭生成器
  20. if __name__=='__main__':
  21. c = consumer() #取得生成器对象,但此处不会执行生成器内部代码
  22. produce(c) # 此处正式生产和执行

协程的一大优势是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

但是只有一个线程,要想利用多核优势时,就需要用多进程+协程的方式解决问题。

多进程,实际上跟多线程真的挺相似的。不过在此之中,我想到一个挺重要的问题,就是线程之间的数据可以通过在整个进程空间中定义共享变量,使用同步方法来同步访问,比如定义一个在线程之间通用的字典对象,也是可行的。但是进程之间呢?数据如何共享,如何同步?

目前想到的方法是,利用文件进行数据交互,还有数据库,MySQL或者Mongo或者Redis等都是进程可以用来同步的方式。MySQL自身带有锁机制,变量的访问是可以同步的。Redis服务器端是单线程,所以也可以实现同步。

然后想到一个比较重要的东西,就是高并发的媒体类网站,通常有多个CGI进程,所以网站的瓶颈不在于语言,而在于数据库I/O。多个进程都会请求数据库事务,这方面才是并发的关键点。此外,用Redis等作为缓存能解决部分I/O带来的问题。

还有一个问题是,一个服务器上运行多个worker进程。前端使用Nginx转发请求,如何转发,进程如何竞争处理这些请求。还有同步I/O,异步I/O,啊,问题真多,不明白的真多。需要把这部分搞懂。明天晚上继续想,可以从Nginx处入手。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注