如何在Python中使用线程?

我正试图理解Python中的线程。我看了文档和例子,但坦率地说,很多例子都过于复杂,我很难理解。

你如何清楚地显示任务被划分为多线程?

这里有一个简单的例子:你需要尝试几个备选的URL,并返回第一个响应的URL的内容。

import Queue
import threading
import urllib2

# called by each thread
def get_url(q, url):
    q.put(urllib2.urlopen(url).read())

theurls = ["http://google.com", "http://yahoo.com"]

q = Queue.Queue()

for u in theurls:
    t = threading.Thread(target=get_url, args = (q,u))
    t.daemon = True
    t.start()

s = q.get()
print s

这是一个将线程作为简单优化的案例。每个子线程都在等待一个URL的解析和响应,以便将其内容放到队列中;每个线程都是一个守护线程(如果主线程结束,不会保持进程--这种情况更常见);主线程启动所有子线程,在队列中进行 "get",等待其中一个子线程完成 "put",然后发出结果并终止(这将使任何可能仍在运行的子线程停机,因为它们是守护线程)。

在Python中正确使用线程总是与I/O操作有关(因为CPython无论如何都不会使用多核来运行CPU绑定的任务,线程的唯一原因是在等待一些I/O时不要阻塞进程)。 队列几乎是将工作分配给线程和/或收集工作结果的最佳方式,而且它们本质上是线程安全的,所以它们使你不必担心锁、条件、事件、semaphores和其他线程间协调/通信概念。

评论(14)

注意。对于Python中的实际并行化,你应该使用multiprocessing模块来分叉多个并行执行的进程(由于全局解释器锁的存在,Python线程提供了交错,但实际上是串行执行的,不是并行的,只有在交错I/O操作时才有用。)

然而,如果你只是在寻找交织(或者正在进行I/O操作,尽管有全局解释器锁,但仍然可以并行化),那么threading模块是开始的地方。作为一个非常简单的例子,让我们考虑通过并行求和子范围来求一个大范围的问题。

import threading

class SummingThread(threading.Thread):
     def __init__(self,low,high):
         super(SummingThread, self).__init__()
         self.low=low
         self.high=high
         self.total=0

     def run(self):
         for i in range(self.low,self.high):
             self.total+=i

thread1 = SummingThread(0,500000)
thread2 = SummingThread(500000,1000000)
thread1.start() # This actually causes the thread to run
thread2.start()
thread1.join()  # This waits until the thread has completed
thread2.join()  
# At this point, both threads have completed
result = thread1.total + thread2.total
print result

请注意,以上是一个非常愚蠢的例子,因为它完全不做I/O,而且由于全局解释器锁的存在,在CPython中会被串行执行,尽管是交错执行(有上下文切换的额外开销)。

评论(8)

像其他人提到的那样,由于GIL的原因,CPython只能对I/O的等待使用线程。 如果你想从多核心的CPU绑定任务中获益,请使用multiprocessing

from multiprocessing import Process

def f(name):
    print 'hello', name

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
评论(5)