对于Python来说,并不缺少并发选项,其标准库包括了对线程、进程和异步I/O的支持。在许多情况下,通过创建诸如异步、线程和子进程之类的高层模块,Python简化了各种并发方法的使用。除了标准库之外,还有一些第三方的解决方案。例如Twisted、Stackless和进程Module。因为GIL,CPU受限的应用程序无法从线程中受益。使用Python时,建议使用进程,或者混合创建进程和线程。
首先弄清楚进程和线程的区别。线程和进程的不同之处在于,它们共享状态、内存和资源。对于线程来说,这个简单的区别既是它的优势,又是它的缺点。一方面,线程是轻量级的,并且相互之间易于通信,但另一方面,它们也带来了包括死锁、争用条件和好复杂性在内的各种问题。幸运的是,由于GIL和队列模块,与采用其他的语言相比,采用Python语言在线程实现的复杂性上要低的多。
《Python开发技术详解》.( 周伟,宗杰).[高清PDF扫描版+随书视频+代码]
一个简单的demo:
#!/usr/bin/env python
import Queue
import threading
import urllib2
import time
hosts = ["http://www.baidu.com", "http://www.sina.com.cn", "http://www.letv.com"]
class ThreadUrl(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
while True:
host = self.queue.get()
url = urllib2.urlopen(host)
print url.read(1024)
self.queue.task_done()
def main():
for i in range(5):
t = ThreadUrl(queue)
t.setDaemon(True)
t.start()
for host in hosts:
queue.put(host)
queue.join()
if __name__ == "__main__":
start = time.time()
main()
print "Elapsed Time:%s" % (time.time() - start)
在Python中使用线程时,这个模型是一种很常见的并且推荐使用的方式。具体工作步骤描述如下:
1. 创建一个Queue.Queue()队列实例,然后向这个队列内灌数据。
2. 将灌进数据的实例传递给线程类,然后通过继承threading.Thread的方式创建。
3. 生成守护进程池(t.setDaemon(True))。
4. 每次从queue中pop一个项目,并使用该线程中的数据和run方法以执行相应的工作。
5. 在完成这项工作之后,使用queue.task_done()方法向任务完成的队列发送一个信号。
6. 对队列执行join操作,实际上意味着等到queue为空,再退出主程序。
在使用这个模式时需要注意一点:通过将守护线程设置为True,将允许主线程或者程序仅在守护线程处于活动状态时才能够退出。这种方式创建了一种简单的方式以控制程序流程,因为在退出之前,你可以对queue执行join操作或者等到队列为空。
说明:
join():保持阻塞状态,直到处理了队列中的所有项目为止。在将一个项目添加到该队列时,未完成的任务的总数就会增加。当使用者线程调用task_done()以表示检索了该项目、并完成了所有的工作时,那么未完成的任务的总数就会减少。当未完成的任务的总数减少到零时,join()就会结束阻塞状态。