为什么生产者 - 消费者不会停止?

问题描述 投票:1回答:1

我找到了一个代表具有两个线程的producer-consumer的示例。但是,当我向进程发送信号停止时,它不会。它期望第二信号,例如SIGKILL完全停止。我认为问题出在task_done()上,但似乎没有。

import time

import queue
import threading
import random


class Producer(threading.Thread):
    """
    Produces random integers to a list
    """

    def __init__(self, queue):
        """
        Constructor.

        @param queue queue synchronization object
        """
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        """
        Thread run method. Append random integers to the integers
        list at random time.
        """
        while True:
            integer = random.randint(0, 256)
            self.queue.put(integer)
            print('%d put to queue by %s' % (integer, self.name))
            time.sleep(1)


class Consumer(threading.Thread):
    """
    Consumes random integers from a list
    """

    def __init__(self, queue):
        """
        Constructor.

        @param integers list of integers
        @param queue queue synchronization object
        """
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        """
        Thread run method. Consumes integers from list
        """
        while True:
            integer = self.queue.get()
            print('%d popped from list by %s' % (integer, self.name))
            self.queue.task_done()


def main():
    q = queue.Queue()
    t1 = Producer(q)
    t2 = Consumer(q)
    t1.start()
    t2.start()
    t1.join()
    t2.join()


if __name__ == '__main__':
    main()

输出:

210 put to queue by Thread-1
210 popped from list by Thread-2
Traceback (most recent call last):
  File "/Users/abc/PycharmProjects/untitled1/ssid.py", line 74, in <module>
    main()
  File "/Users/abc/PycharmProjects/untitled1/ssid.py", line 69, in main
    t1.join()
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 1056, in join
    self._wait_for_tstate_lock()
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 1072, in _wait_for_tstate_lock
    elif lock.acquire(block, timeout):
KeyboardInterrupt
244 put to queue by Thread-1
244 popped from list by Thread-2
85 put to queue by Thread-1
85 popped from list by Thread-2
160 put to queue by Thread-1
160 popped from list by Thread-2
python python-3.x multithreading queue producer-consumer
1个回答
0
投票

这是因为只有主线程被KeyboardInterrupt停止了。您可以通过让您的子线程打印threading.enumerate()来观察这一点,import time import queue import threading import random class Producer(threading.Thread): def __init__(self, queue): super().__init__() self.queue = queue def run(self): while True: integer = random.randint(0, 256) self.queue.put(integer) print(f'{integer} put to queue by {self.name} ' f'threads: {threading.enumerate()}') time.sleep(1) class Consumer(threading.Thread): def __init__(self, queue): super().__init__() self.queue = queue def run(self): while True: integer = self.queue.get() print(f'{integer} popped from list by {self.name} ' f'threads:{threading.enumerate()}') self.queue.task_done() def main(): q = queue.Queue() t1 = Producer(q) t2 = Consumer(q) # t1.daemon = True # t2.daemon = True t1.start() t2.start() t1.join() t2.join() if __name__ == '__main__': try: main() except KeyboardInterrupt: print('got KeyboardInterrupt') 返回所有活动线程+主线程。

97 put to queue by Thread-1 threads: [<_MainThread(MainThread, started 
139810293606208)>, <Producer(Thread-1, started 139810250913536)>, 
<Consumer(Thread-2, started 139810242520832)>]
97 popped from list by Thread-2 threads:[<_MainThread(MainThread, started 
139810293606208)>, <Producer(Thread-1, started 139810250913536)>, 
<Consumer(Thread-2, started 139810242520832)>]
got KeyboardInterrupt
92 put to queue by Thread-1 threads: [<_MainThread(MainThread, stopped 
139810293606208)>, <Producer(Thread-1, started 139810250913536)>, 
<Consumer(Thread-2, started 139810242520832)>]
92 popped from list by Thread-2 threads:[<_MainThread(MainThread, stopped 
139810293606208)>, <Producer(Thread-1, started 139810250913536)>, 
<Consumer(Thread-2, started 139810242520832)>]

使用KeyboardInterrupt输出示例。注意在KeyboardInterrupt之后列为'stopped'的MainThread:

docs

您可以使子线程守护程序让它们与主线程一起退出。但是,只有在您的线程没有任何资源的情况下才应该考虑:

注意守护程序线程在关闭时突然停止。他们的资源(例如打开文件,数据库事务等)可能无法正确发布。如果您希望线程正常停止,请使它们成为非守护进程并使用合适的信号机制,例如事件KeyboardInterrupt

更好的方法是像上面的代码一样捕获qazxswpoi,并在队列中向子线程发送一个sentinel值,让他们知道他们应该完成,允许他们在退出之前进行清理。

© www.soinside.com 2019 - 2024. All rights reserved.