【问题标题】:Multiple stdout w/ flush going on in Python threading在 Python 线程中进行多个带刷新的标准输出
【发布时间】:2017-05-19 02:34:04
【问题描述】:

我编写了一小段代码来测试并希望能够调试问题,而无需在 Python 中修改我的主小程序中的代码。这让我可以构建这段代码:

#!/usr/bin/env python
import sys, threading, time

def loop1():
    count = 0
    while True:
        sys.stdout.write('\r thread 1: ' + str(count))
        sys.stdout.flush()
        count = count + 1
        time.sleep(.3)
        pass
    pass

def loop2():
    count = 0
    print ""
    while True:
        sys.stdout.write('\r thread 2: ' + str(count))
        sys.stdout.flush()
        count = count + 2
        time.sleep(.3)
    pass

if __name__ == '__main__':
    try:
        th = threading.Thread(target=loop1)
        th.start()

        th1 = threading.Thread(target=loop2)
        th1.start()
        pass
    except KeyboardInterrupt:
        print ""
        pass
    pass

我对这段代码的目标是能够让这两个线程同时以 stdout 格式(带刷新)显示输出,然后并排显示。问题是我假设因为它正在刷新每个字符串,所以默认情况下它会刷新另一个字符串。如果可能的话,我不知道如何让它工作。

如果您只运行其中一个线程,它就可以正常工作。但是,我希望能够在终端输出中同时运行它们自己的字符串来运行两个线程。这是一张显示我得到的图片:

终端截图

如果您需要更多信息,请告诉我。提前致谢。

【问题讨论】:

  • 打印到标准输出不是线程安全的。要么使用logging 模块,实现某种锁定,要么将打印移至另一个线程。
  • 我使用 stdout 是因为我喜欢它如何显示(例如)一个数字,然后刷新它并在旧位置打印一个新数字。因此,为输出创建一个固定点,而不是让它为每个数字创建一个新行来打印它。

标签: python multithreading stdout


【解决方案1】:

与其让每个线程输出到标准输出,更好的解决方案是让一个线程独占控制标准输出。然后为其他线程提供线程安全通道来调度要输出的数据。

实现此目的的一个好方法是在所有线程之间共享Queue。确保在将数据添加到队列后,只有输出线程正在访问数据。

输出线程可以存储来自其他线程的最后一条消息,并使用该数据很好地格式化标准输出。这可以包括清除输出以显示类似的内容,并在每个线程生成新数据时对其进行更新。

Threads
#1: 0
#2: 0

#示例 请注意,将参数传递给线程有一些陷阱,以及我使用一些技巧来确保安全退出,例如使线程成为守护进程。没有使用换行符,因为\r 回车只重新开始当前的输出行。

import queue, threading
import time, sys

q = queue.Queue()
keepRunning = True

def loop_output():
    thread_outputs = dict()

    while keepRunning:
        try:
            thread_id, data = q.get_nowait()
            thread_outputs[thread_id] = data
        except queue.Empty:
            # because the queue is used to update, there's no need to wait or block.
            pass

        pretty_output = ""
        for thread_id, data in thread_outputs.items():
            pretty_output += '({}:{}) '.format(thread_id, str(data))

        sys.stdout.write('\r' + pretty_output)
        sys.stdout.flush()
        time.sleep(1)

def loop_count(thread_id, increment):
    count = 0
    while keepRunning:
        msg = (thread_id, count)
        try:
            q.put_nowait(msg)
        except queue.Full:
            pass

        count = count + increment
        time.sleep(.3)
        pass
    pass

if __name__ == '__main__':
    try:
        th_out = threading.Thread(target=loop_output)
        th_out.start()

        # make sure to use args, not pass arguments directly
        th0 = threading.Thread(target=loop_count, args=("Thread0", 1))
        th0.daemon = True
        th0.start()

        th1 = threading.Thread(target=loop_count, args=("Thread1", 3))
        th1.daemon = True
        th1.start()

        # Keep the main thread alive to wait for KeyboardInterrupt
        while True:
            time.sleep(.1)

    except KeyboardInterrupt:
        print("Ended by keyboard stroke")
        keepRunning = False
        for th in [th0, th1]:
            th.join()

示例输出:

(Thread0:110) (Thread1:330)

【讨论】:

  • @BlackVikingPro 顺便欢迎来到 StackOverflow!如果任何答案有助于解决您的问题或为您提供有用的见解,请确保在问题得到解决后选择最佳答案:)
  • 你真的需要让线程守护进程吗?
  • 并且while True:可能存在缩进错误
猜你喜欢
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多