如何让Python线程优雅地完成

Isa*_*ell 6 python multithreading python-2.7

我正在做一个涉及数据收集和日志记录的项目.我有两个运行的线程,一个集合线程和一个日志记录线程,都在main中启动.我正在尝试使用Ctrl-C允许程序正常终止.

我正在使用a threading.Event向线程发出信号以结束它们各自的循环.它可以正常停止该sim_collectData方法,但它似乎没有正确停止该logData线程.该Collection terminated打印语句永远不会执行,程序公正摊位.(它没有结束,只是坐在那里).

第二个while循环logData是确保记录队列中的所有内容.目标是让Ctrl-C立即停止收集线程,然后允许日志记录线程完成清空队列,然后才完全终止程序.(现在,数据刚刚被打印出来 - 最终它将被记录到数据库中).

我不明白为什么第二个线程永远不会终止.我基于我在这个答案上所做的事情:在一段时间后停止一个线程.我错过了什么?

def sim_collectData(input_queue, stop_event):
    ''' this provides some output simulating the serial
    data from the data logging hardware. 
    '''
    n = 0
    while not stop_event.is_set():
        input_queue.put("DATA: <here are some random data> " + str(n))
        stop_event.wait(random.randint(0,5))
        n += 1
    print "Terminating data collection..."
    return

def logData(input_queue, stop_event):
    n = 0

    # we *don't* want to loop based on queue size because the queue could
    # theoretically be empty while waiting on some data.
    while not stop_event.is_set():
        d = input_queue.get()
        if d.startswith("DATA:"):
            print d
        input_queue.task_done()
        n += 1

    # if the stop event is recieved and the previous loop terminates, 
    # finish logging the rest of the items in the queue.
    print "Collection terminated. Logging remaining data to database..."
    while not input_queue.empty():
        d = input_queue.get()
        if d.startswith("DATA:"):
            print d
        input_queue.task_done()
        n += 1
    return


def main():
    input_queue = Queue.Queue()

    stop_event = threading.Event() # used to signal termination to the threads

    print "Starting data collection thread...",
    collection_thread = threading.Thread(target=sim_collectData, args=(input_queue,     stop_event))
    collection_thread.start()
    print "Done."

    print "Starting logging thread...",
    logging_thread = threading.Thread(target=logData, args=(input_queue, stop_event))
    logging_thread.start()
    print "Done."

    try:
        while True:
        time.sleep(10)
    except (KeyboardInterrupt, SystemExit):
        # stop data collection. Let the logging thread finish logging everything in the queue
        stop_event.set()

 main()
Run Code Online (Sandbox Code Playgroud)

tde*_*ney 8

问题是您的记录器正在等待d = input_queue.get()并且不会检查事件.一种解决方案是完全跳过事件并发明一条告诉记录器停止的唯一消息.收到信号后,将该消息发送到队列.

import threading
import Queue
import random
import time

def sim_collectData(input_queue, stop_event):
    ''' this provides some output simulating the serial
    data from the data logging hardware. 
    '''
    n = 0
    while not stop_event.is_set():
        input_queue.put("DATA: <here are some random data> " + str(n))
        stop_event.wait(random.randint(0,5))
        n += 1
    print "Terminating data collection..."
    input_queue.put(None)
    return

def logData(input_queue):
    n = 0

    # we *don't* want to loop based on queue size because the queue could
    # theoretically be empty while waiting on some data.
    while True:
        d = input_queue.get()
        if d is None:
            input_queue.task_done()
            return
        if d.startswith("DATA:"):
            print d
        input_queue.task_done()
        n += 1

def main():
    input_queue = Queue.Queue()

    stop_event = threading.Event() # used to signal termination to the threads

    print "Starting data collection thread...",
    collection_thread = threading.Thread(target=sim_collectData, args=(input_queue,     stop_event))
    collection_thread.start()
    print "Done."

    print "Starting logging thread...",
    logging_thread = threading.Thread(target=logData, args=(input_queue,))
    logging_thread.start()
    print "Done."

    try:
        while True:
            time.sleep(10)
    except (KeyboardInterrupt, SystemExit):
        # stop data collection. Let the logging thread finish logging everything in the queue
        stop_event.set()

main()
Run Code Online (Sandbox Code Playgroud)