2012-07-23 56 views
3

python新手,试图理解多线程。下面是一个来自python文档的例子,关于Queue基本python多线程问题

对于我生活中的这些,我不明白这个例子是如何工作的。在worker()函数中,有一个无限循环。工人如何知道什么时候离开循环?似乎没有破裂的情况。

到底什么是联结?我不应该加入线程吗?

def worker(): 
    while True: 
     item = q.get() 
     do_work(item) 
     q.task_done() 

q = Queue() 
for i in range(num_worker_threads): 
    t = Thread(target=worker) 
    t.daemon = True 
    t.start() 

for item in source(): 
    q.put(item) 

q.join()  # block until all tasks are done 

另外一个问题,什么时候应该使用多线程,何时应该使用多处理?

回答

6

是。你是对的。 worker将永远运行。但是由于队列只有有限数量的项目,最终worker将永久阻止在q.get()(因为队列中不会有更多项目)。在这一点上,worker仍在运行是无关紧要的。 q.join()阻塞,直到队列计数下降到0(无论工作者线程调用q.task_done,计数下降1)。之后,程序结束。而无限阻塞的线程与它的创造者一起死亡。

+0

啊我现在明白了。有什么网站/资源可以阅读这些微妙之处吗?文件真的不是它看起来最好的地方。 – JZee 2012-07-23 21:19:17

+1

除了主线程的末尾杀死子线程之外,所有这些信息都直接从您链接自己的'Queue'文档页面。 – 2012-07-23 21:20:16

+0

感谢您的回答。我想我会接受它,因为它回答了我的主要问题。 – JZee 2012-07-24 17:24:45

5

关于第二个问题,Python中线程和进程之间最大的区别是主流实现使用全局解释器锁(GIL)来确保多个线程不能混淆Python的内部数据结构。这意味着对于花费大部分时间在纯Python中进行计算的程序,即使使用多个CPU,也不会加快程序的运行速度,因为一次只有一个线程可以保存GIL。另一方面,多线程可以轻松地在Python程序中共享数据,而在一些情况下(但绝非全部),您不必过多担心线程安全问题。

多线程可以加快Python程序的运行速度,当程序花费大部分时间等待I/O - 磁盘访问或尤其是网络操作时。在执行I/O操作时,GIL不被占用,因此很多Python线程可以在I/O绑定的应用程序中同时运行。另一方面,通过多处理,每个进程都有自己的GIL,因此您的性能可以扩展到可用的CPU核心数量。不利的一面是进程之间的所有通信都必须通过multiprocessing.Queue完成(它在表面上非常像Queue.Queue,但由于它必须跨进程边界进行通信,因此具有非常不同的底层机制)。

因为通过线程安全或进程间队列工作避免了很多潜在的线程问题,并且由于Python使得它非常容易,所以multiprocessing模块非常有吸引力。

+0

很好的回答,这是一个耻辱,因为我刚刚注册,所以无法投票。我正在做多线程/处理,因为我需要并行读取多个数据文件(数以千计),从中读取一些信息,然后将文件写回到磁盘上(再次以千为单位)。那么我应该使用多线程还是多处理?似乎我会做相当数量的I/O。 – JZee 2012-07-23 21:39:25

+0

而且我也不清楚如何“加入”这些过程。我在最后有一个循环,循环遍历每个创建的进程,并调用join,但是我的代码仍然没有终止。 – JZee 2012-07-23 22:14:05

+0

如果主线程调用'childThread.join()',则子线程必须自行终止,以上示例代码不会执行此操作。你的示例代码使用'Queue.join()',这是非常不同的。现在可能是采取现有代码并提出新问题的时候了。 – 2012-07-23 22:53:01

0

同意joel-cornett,主要是。我试图在python2.7运行下面的代码片段:

from threading import Thread 
from Queue import Queue 

def worker(): 
    def do_work(item): 
     print(item) 

    while True: 
     item = q.get() 
     do_work(item) 
     q.task_done() 

q = Queue() 
for i in range(4): 
    t = Thread(target=worker) 
    t.daemon = True 
    t.start() 

for item in range(10): 
    q.put(item) 

q.join() 

输出是:

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
Exception in thread Thread-3 (most likely raised during interpreter shutdown): 
Traceback (most recent call last): 
    File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner 
    File "/usr/lib/python2.7/threading.py", line 504, in run 
    File "abc.py", line 9, in worker 
    File "/usr/lib/python2.7/Queue.py", line 168, in get 
    File "/usr/lib/python2.7/threading.py", line 236, in wait 
<type 'exceptions.TypeError'>: 'NoneType' object is not callable 

最可能的解释,我认为:

随着队列任务用尽后再变空,父线程从q.join()返回并退出队列后退出。子线程在接收到“item = q.get()”中产生的第一个TypeError异常时终止,因为队列不再存在。

+0

如果我删除'q.join()'并确保父线程在处理完成之前死亡,我的结果与您相同。 – 2012-07-23 21:43:59