Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/359.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何让Python线程优雅地结束_Python_Multithreading_Python 2.7 - Fatal编程技术网

如何让Python线程优雅地结束

如何让Python线程优雅地结束,python,multithreading,python-2.7,Python,Multithreading,Python 2.7,我正在做一个涉及数据收集和记录的项目。我有两个线程在运行,一个收集线程和一个日志线程,都是从main开始的。我试图允许程序在使用Ctrl-C时优雅地终止 我正在使用threading.Event向线程发送信号,以结束各自的循环。停止sim\u collectData方法工作正常,但似乎没有正确地停止logData线程。Collection-terminatedprint语句永远不会执行,程序只是暂停。(它没有结束,只是坐在那里) logData中的第二个while循环是确保记录队列中的所有内容。

我正在做一个涉及数据收集和记录的项目。我有两个线程在运行,一个收集线程和一个日志线程,都是从main开始的。我试图允许程序在使用Ctrl-C时优雅地终止

我正在使用
threading.Event
向线程发送信号,以结束各自的循环。停止
sim\u collectData
方法工作正常,但似乎没有正确地停止
logData
线程。
Collection-terminated
print语句永远不会执行,程序只是暂停。(它没有结束,只是坐在那里)

logData
中的第二个
while
循环是确保记录队列中的所有内容。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()
def sim卡采集数据(输入队列、停止事件):
''这提供了一些模拟串行通信的输出
来自数据记录硬件的数据。
'''
n=0
而不是停止\u事件。是否设置了\u():
输入_queue.put(“数据:+str(n))
停止事件。等待(random.randint(0,5))
n+=1
打印“正在终止数据收集…”
返回
def日志数据(输入队列、停止事件):
n=0
#我们*不*希望根据队列大小进行循环,因为队列可能
#理论上,在等待某些数据时必须为空。
而不是停止\u事件。是否设置了\u():
d=输入_队列.get()
如果d.startswith(“数据:”):
打印d
输入队列。任务完成()
n+=1
#如果接收到停止事件且前一个循环终止,
#完成队列中其余项目的日志记录。
打印“收集已终止。将剩余数据记录到数据库…”
不输入时\u queue.empty():
d=输入_队列.get()
如果d.startswith(“数据:”):
打印d
输入队列。任务完成()
n+=1
返回
def main():
input_queue=queue.queue()
stop_event=threading.event()#用于向线程发出终止信号
打印“正在启动数据采集线程…”,
collection\u thread=threading.thread(target=sim\u collectData,args=(输入队列,停止事件))
集合_thread.start()
打印“完成”
打印“正在启动日志线程…”,
日志记录线程=线程。线程(目标=日志数据,参数=(输入队列,停止事件))
日志记录_thread.start()
打印“完成”
尝试:
尽管如此:
时间。睡眠(10)
除了(键盘中断、系统退出):
#停止数据收集。让日志线程完成记录队列中的所有内容
stop_event.set()
main()

您正在调用
输入队列上的阻塞get,没有超时。在
logData
的任一部分中,如果调用
input\u queue.get()
且队列为空,它将无限期阻塞,从而阻止
logging\u线程
完成

要修复此问题,您需要调用
input\u queue.get\u nowait()
或将超时传递给
input\u queue.get()

以下是我的建议:

def logData(input_queue, stop_event):
    n = 0

    while not stop_event.is_set():
        try:
            d = input_queue.get_nowait()
            if d.startswith("DATA:"):
                print "LOG: " + d
                n += 1
        except Queue.Empty:
            time.sleep(1)
    return
您也在向线程发送终止信号,但不是等待线程终止。考虑在<<代码>主< /代码>函数中执行此操作。< /P>
try:
    while True:
        time.sleep(10)
except (KeyboardInterrupt, SystemExit):
    stop_event.set()
    collection_thread.join()
    logging_thread.join()

我不是线程方面的专家,但在您的
logData
函数中,第一个
d=input\u queue.get()
正在阻塞,即,如果队列为空,它将永远等待,直到收到队列消息。这可能就是
logData
线程从未终止的原因,它一直在等待队列消息


请参阅[Python文档]将其更改为非阻塞队列:使用
.get(False)
.get\u nowait()
-但在队列为空的情况下,这两者都需要一些异常处理。

问题在于日志记录器正在等待
d=input\u 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()
导入线程
导入队列
随机输入
导入时间
def sim_采集数据(输入队列、停止事件):
''这提供了一些模拟串行通信的输出
来自数据记录硬件的数据。
'''
n=0
而不是停止\u事件。是否设置了\u():
输入_queue.put(“数据:+str(n))
停止事件。等待(random.randint(0,5))
n+=1
打印“正在终止数据收集…”
输入队列。放置(无)
返回
def日志数据(输入队列):
n=0
#我们*不*希望根据队列大小进行循环,因为队列可能
#理论上,在等待某些数据时必须为空。
尽管如此:
d=输入_队列.get()
如果d为无:
输入队列。任务完成()
返回
如果d.startswith(“数据:”):
打印d
输入队列。任务完成()
n+=1
def main():
input_queue=queue.queue()
stop_event=threading.event()#用于向线程发出终止信号
打印“正在启动数据采集线程…”,
collection\u thread=threading.thread(target=sim\u collectData,args=(输入队列,停止事件))
集合_thread.start()
打印“完成”
打印“正在启动日志线程…”,
logging\u thread=threading.thread(target=logData,args=(input\u queue,))
日志记录_thread.start()
打印“完成”
尝试:
尽管如此:
时间。睡眠(10)
除了(键盘中断、系统退出):
#停止数据收集。让日志线程完成记录队列中的所有内容
stop_event.set()
main()
基于answe
import threading, queue

from functools import total_ordering
@total_ordering
class Final:
    def __repr__(self):
        return "∞"

    def __lt__(self, other):
        return False

    def __eq__(self, other):
        return isinstance(other, Final)

Infty = Final()

class IterQueue(queue.Queue):
    def __init__(self):
        self.lock = threading.Lock()
        self.stopped = False
        self.getters = 0
        super().__init__()

    def __iter__(self):
        return self

    def get(self):
        raise NotImplementedError("This queue may only be used as an iterator.")

    def __next__(self):
        with self.lock:
            if self.stopped:
                raise StopIteration
            self.getters += 1
        data = super().get()
        if data == Infty:
            self.task_done()
            raise StopIteration
        with self.lock:
            self.getters -= 1
        return data

    def stop(self):
        self.join()
        self.stopped = True
        with self.lock:
            for i in range(self.getters):
                self.put(Infty)
        self.join()

class IterPriorityQueue(IterQueue, queue.PriorityQueue):
    pass
import threading, Queue

from functools import total_ordering
@total_ordering
class Final:
    def __repr__(self):
        return "Infinity"

    def __lt__(self, other):
        return False

    def __eq__(self, other):
        return isinstance(other, Final)

Infty = Final()

class IterQueue(Queue.Queue, object):
    def __init__(self):
        self.lock = threading.Lock()
        self.stopped = False
        self.getters = 0
        super(IterQueue, self).__init__()

    def __iter__(self):
        return self

    def get(self):
        raise NotImplementedError("This queue may only be used as an iterator.")

    def next(self):
        with self.lock:
            if self.stopped:
                raise StopIteration
            self.getters += 1
        data = super(IterQueue, self).get()
        if data == Infty:
            self.task_done()
            raise StopIteration
        with self.lock:
            self.getters -= 1
        return data

    def stop(self):
        self.join()
        self.stopped = True
        with self.lock:
            for i in range(self.getters):
                self.put(Infty)
        self.join()

class IterPriorityQueue(IterQueue, Queue.PriorityQueue):
    pass
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..."
    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.
    for d in input_queue:
        if d.startswith("DATA:"):
            print d
        input_queue.task_done()
        n += 1

def main():
    input_queue = IterQueue()

    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()
        input_queue.stop()

main()