3

刚接触 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

还有一个问题,什么时候应该使用多线程,什么时候应该使用多处理?

4

3 回答 3

6

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

于 2012-07-23T21:07:14.357 回答
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模块非常有吸引力。

于 2012-07-23T21:19:31.160 回答
0

同意乔尔-科内特的观点,主要是。我尝试在 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 异常时终止,因为队列不再存在。

于 2012-07-23T21:34:33.220 回答