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

多线程CLI聊天工具终端输出同步问题求助

解决CLI实时聊天工具的多线程终端输出混乱问题

问题根源

接收、发送线程同时对终端进行IO操作且无同步机制:

  • 发送线程等待input()时,终端显示用户名提示符
  • 接收线程此时输出消息会打断当前输入行,造成提示符重复、消息错位

解决方案:用线程锁同步终端IO

所有涉及终端输出/输入的操作必须加锁,保证同一时间只有一个线程能操作终端。

步骤1:定义全局线程锁

在代码开头添加:

import threading
terminal_lock = threading.Lock()

步骤2:修改输出函数,加锁执行

修改print_message、print_simple_message,确保输出操作互斥:

def print_message(message, sender_username):
    global username
    with terminal_lock:
        print(f"{sender_username}{message}")
        # 重新打印提示符,固定光标位置
        print(colorama.Fore.GREEN + username + colorama.Fore.RESET, end='', flush=True)

def print_simple_message(message, sender_username):
    with terminal_lock:
        print(f"{sender_username}: {message}")
        print(colorama.Fore.GREEN + username + colorama.Fore.RESET, end='', flush=True)

步骤3:修改发送线程的输入逻辑

将打印提示符和获取输入的过程加锁,避免被接收线程打断:

def send_messages(sock):
    global finished
    while not finished:
        with terminal_lock:
            print(colorama.Fore.GREEN + username + colorama.Fore.RESET, end='', flush=True)
            message = input()
        mes = message
        encrypt_message(sock, message)
        if mes == "exit()":
            with terminal_lock:
                print("You have left the room.")
            finished = True
            break

步骤4:修正接收线程的终端操作

接收线程中的换行、异常输出也需要加锁:

def receive_messages(sock):
    global finished
    while not finished:
        try:
            raw_message = sock.recv(1024).decode()
            with terminal_lock:
                # 换行将当前输入行顶出,避免消息覆盖提示符
                print()
            raw_message = json.loads(raw_message)
            message_val = raw_message.get("Message")
            sender_username, _, message = message_val.partition(':')

            # 消息类型处理逻辑保持不变
            if raw_message.get("Message_type") == MESSAGE_TYPE_JOIN_ROOM:
                message = " is connected"
            if raw_message.get("Message_type") == MESSAGE_TYPE_PUBLIC_KEY:
                if store_public_key(raw_message.get("Message")):
                    continue
                message = " public key received"
                send_public_key(sock)
            if raw_message.get("Message_type") == MESSAGE_TYPE_LEAVE_ROOM:
                message = " is disconnected from room"
            if raw_message.get("Message_type") == MESSAGE_TYPE_SIMPLE_MESSAGE:
                message = decrypt_message(message)
                print_simple_message(message, sender_username)
                continue

            print_message(message, sender_username)

        except:
            with terminal_lock:
                print("An error occurred. Exiting.")
            finished = True
            break

关键细节

  • 加flush=True确保输出立即刷新到终端,避免缓存延迟导致的显示异常
  • 每次输出消息后重新打印用户名提示符,保证光标回到正确输入位置
  • 所有终端IO操作(print/input)都通过锁互斥,彻底避免并发冲突

优化后输出示例

user:~/CLI_Chat_Tool$ python3 client.py private harry
Connected to the server.
@harry:
@sam is connected
@harry:
@sam public key received
@harry:hello
@sam: hey
@harry:hi
@harry:

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 21:00:32