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
另外一个问题,何时应该使用多线程以及何时应该使用多处理?
答案 0 :(得分:6)
烨。你是对的。 worker
将永远运行。但是,由于Queue只有有限数量的项目,最终worker
将永久阻塞q.get()
(因为队列中不再有项目)。此时,worker
仍然在运行,这是无关紧要的。 q.join()
阻塞,直到队列计数降为0(每当工作线程调用q.task_done
时,计数下降1)。之后,程序结束。无限阻塞的线程与它的创建者一起消失。
答案 1 :(得分: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
模块非常有吸引力。
答案 2 :(得分: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异常时终止,因为队列不再存在。