@chyoo1991
2015-09-21T15:41:13.000000Z
字数 2242
阅读 3608
python coding
从某种角度看,进程与线程的形式很像,至少在Python里面,
threading.Thread类与multiprocess.Process的方法还是挺像的。有些东西,往深入了理解,就能发现很多细节上的不同。
import urllib2import timefrom threading import ThreadURLs = ["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() - startclass URL(Thread):"""定义多线程访问url的类"""def __init__(self, url):self.url = urlsuper(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() - startif __name__ == '__main__':normal_way()multi_process_way()
经过测算,上述代码,在70个URL的时候,返回结果是:
6.29869413376 # 第一种单线程方式 单位是秒0.443621873856 # 第二种多线程方式
由此可见,多线程在处理I/O的时候非常有优势,即使只用了一个核心。
但是多线程本身也有问题。除了GIL这个东西之外,当线程多的时候,线程的切换是一件十分耗时的工作。但是python里面提供了一个比较好玩的东西:协程,来解决这个问题。
协程是一个很特别的东西,其执行函数之间的切换,由函数自身控制。其本身的执行,是在单一线程中。可以设置不同的函数,执行不同的任务,函数之间的切换,通过yield实现。一个生产者与消费者的实例:
import timedef consumer():r = ''while True:n = yield r # 通过yield获得n的值,从另一个函数里面传进来,传出去r的值if not n:returnprint('[CONSUMER] Consuming %s...' % n)time.sleep(1)r = '200 OK'def produce(c):c.next() # c 是生成器对象(含有yield的函数就是生成器), 此句意思是启动生成器n = 0while n < 5:n = n + 1print('[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处入手。