You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Shell中优雅实现并发任务的实时状态输出?

解决多线程设备状态输出混乱的优雅方案

1. 利用ANSI终端转义序列实现固定行更新

给每个设备分配终端内的固定行,更新状态时通过光标移动回到该行开头,覆盖原有内容,实现单设备状态的实时刷新。

示例代码:

import concurrent.futures
import sys
from itertools import repeat

# 为每个设备分配固定行号
device_lines = {dev_id: i+1 for i, dev_id in enumerate(ids)}

def device_ctl(dev_id, args):
    def update_status(status):
        line_num = device_lines[dev_id]
        # 移动光标到指定行开头,打印状态并用空格覆盖旧内容
        sys.stdout.write(f"\033[{line_num};1H{dev_id} {status:<30}")
        sys.stdout.flush()

    update_status("Connecting...")
    # 模拟设备通信逻辑
    # ...
    update_status("Connected! Status: Idle")
    # 后续状态更新直接调用update_status即可
    # ...

with concurrent.futures.ThreadPoolExecutor(20) as executor:
    executor.map(device_ctl, ids, repeat(args))

# 最后将光标移到所有设备行下方,避免后续输出覆盖状态
sys.stdout.write(f"\033[{len(ids)+1};1H\n")
sys.stdout.flush()

2. 单线程统一处理输出(生产者-消费者模式)

创建专门的输出线程,所有设备线程仅负责收集状态并发送到队列,由输出线程统一维护和刷新终端内容,彻底避免多线程竞争输出资源。

示例代码:

import concurrent.futures
import queue
import threading
from itertools import repeat

status_queue = queue.Queue()
device_status = {}

def output_worker():
    while True:
        try:
            dev_id, status = status_queue.get()
            if dev_id is None:  # 接收结束信号
                break
            device_status[dev_id] = status
            # 清屏后重新打印所有设备状态(适合设备数量不多的场景)
            print("\033[H\033[J", end="")
            for dev in device_status:
                print(f"{dev} {device_status[dev]}")
            status_queue.task_done()
        except Exception:
            break

# 启动输出线程
output_thread = threading.Thread(target=output_worker)
output_thread.start()

def device_ctl(dev_id, args):
    status_queue.put((dev_id, "Connecting..."))
    # 模拟设备通信逻辑
    # ...
    status_queue.put((dev_id, "Connected! Status: Idle"))
    # 后续状态更新同理
    # ...

with concurrent.futures.ThreadPoolExecutor(20) as executor:
    executor.map(device_ctl, ids, repeat(args))

# 发送结束信号并等待输出线程完成
status_queue.put((None, None))
output_thread.join()

3. 使用第三方终端库简化实现

借助rich这类封装好的终端UI库,无需手动处理ANSI序列,快速实现美观的实时状态展示。

示例代码(需先安装:pip install rich):

from rich.live import Live
from rich.table import Table
import concurrent.futures
from itertools import repeat
import threading

# 初始化状态表格
status_table = Table(show_header=False)
for dev_id in ids:
    status_table.add_row(dev_id, "Connecting...")

table_lock = threading.Lock()

def device_ctl(dev_id, args):
    with table_lock:
        # 定位对应设备行并更新状态
        for row in status_table.rows:
            if row.cells[0].value == dev_id:
                row.cells[1].value = "Connected! Status: Idle"
                break
    # 后续状态更新同理
    # ...

# 启动实时展示
with Live(status_table, refresh_per_second=4):
    with concurrent.futures.ThreadPoolExecutor(20) as executor:
        executor.map(device_ctl, ids, repeat(args))

内容的提问来源于stack exchange,提问作者André

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 05:05:25