@chyoo1991
2015-09-21T15:41:13.000000Z
字数 2242
阅读 3540
python
coding
从某种角度看,进程与线程的形式很像,至少在Python里面,
threading.Thread
类与multiprocess.Process
的方法还是挺像的。有些东西,往深入了理解,就能发现很多细节上的不同。
import urllib2
import time
from threading import Thread
URLs = ["http://www.baidu.com",
"https://www.taobao.com/",
"http://weibo.com/hi/",
"http://www.jd.com/"
...] # 在大量访问url的时候,多线程很有优势,实际上也是空间换时间
def normal_way():
start = time.time()
for url in URLs:
urllib2.urlopen(url)
print time.time() - start
class URL(Thread):
"""
定义多线程访问url的类
"""
def __init__(self, url):
self.url = url
super(URL, self).__init__()
def run(self):
urllib2.urlopen(self.url)
def multi_process_way():
num = len(URLs)
threads = []
start = time.time()
for i in xrange(num):
t = URL(URLs[i])
threads.append(t)
t.start()
for i in xrange(num):
threads[i].join() # 等线程执行完毕再执行主线程,保证时间上的统计是没问题的
print time.time() - start
if __name__ == '__main__':
normal_way()
multi_process_way()
经过测算,上述代码,在70个URL的时候,返回结果是:
6.29869413376 # 第一种单线程方式 单位是秒
0.443621873856 # 第二种多线程方式
由此可见,多线程在处理I/O的时候非常有优势,即使只用了一个核心。
但是多线程本身也有问题。除了GIL这个东西之外,当线程多的时候,线程的切换是一件十分耗时的工作。但是python里面提供了一个比较好玩的东西:协程,来解决这个问题。
协程是一个很特别的东西,其执行函数之间的切换,由函数自身控制。其本身的执行,是在单一线程中。可以设置不同的函数,执行不同的任务,函数之间的切换,通过yield
实现。一个生产者与消费者的实例:
import time
def consumer():
r = ''
while True:
n = yield r # 通过yield获得n的值,从另一个函数里面传进来,传出去r的值
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
time.sleep(1)
r = '200 OK'
def produce(c):
c.next() # c 是生成器对象(含有yield的函数就是生成器), 此句意思是启动生成器
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n) # 发送n 到生成器c,并取得生成器执行的结果的值,即r值
print('[PRODUCER] Consumer return: %s' % r)
c.close() # 关闭生成器
if __name__=='__main__':
c = consumer() #取得生成器对象,但此处不会执行生成器内部代码
produce(c) # 此处正式生产和执行
协程的一大优势是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
但是只有一个线程,要想利用多核优势时,就需要用多进程+协程的方式解决问题。
多进程,实际上跟多线程真的挺相似的。不过在此之中,我想到一个挺重要的问题,就是线程之间的数据可以通过在整个进程空间中定义共享变量,使用同步方法来同步访问,比如定义一个在线程之间通用的字典对象,也是可行的。但是进程之间呢?数据如何共享,如何同步?
目前想到的方法是,利用文件进行数据交互,还有数据库,MySQL或者Mongo或者Redis等都是进程可以用来同步的方式。MySQL自身带有锁机制,变量的访问是可以同步的。Redis服务器端是单线程,所以也可以实现同步。
然后想到一个比较重要的东西,就是高并发的媒体类网站,通常有多个CGI进程,所以网站的瓶颈不在于语言,而在于数据库I/O。多个进程都会请求数据库事务,这方面才是并发的关键点。此外,用Redis等作为缓存能解决部分I/O带来的问题。
还有一个问题是,一个服务器上运行多个worker进程。前端使用Nginx转发请求,如何转发,进程如何竞争处理这些请求。还有同步I/O,异步I/O,啊,问题真多,不明白的真多。需要把这部分搞懂。明天晚上继续想,可以从Nginx处入手。