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

Tkinter多线程控制问题:按Listbox顺序并发打印名称

问题描述

我有一个简单的Listbox,其中按字母顺序添加了一组名称。我希望为每个名称创建一个线程来并发打印,但需严格遵循Listbox中的原有顺序。

尝试的代码如下:

from tkinter import Tk, Button, Listbox
import threading

class MainWindow(Tk):
  def __init__(self):
    super().__init__()

    self.lb1 = Listbox(self, width=26, cursor='hand2')
    self.lb1.pack(side='left', fill='y', padx=20, pady=20)

    self.b1 = Button(self, text='START', bg='green', fg='white', cursor='hand2', command=self.start_thread)
    self.b1.pack(side='left')

    self.lb1.insert('end', 'Aaron')
    self.lb1.insert('end', 'Billy')
    self.lb1.insert('end', 'Chris')
    self.lb1.insert('end', 'David')
    self.lb1.insert('end', 'Edward')
    self.lb1.insert('end', 'Frank')
    self.lb1.insert('end', 'George')
    self.lb1.insert('end', 'Howard')
    self.lb1.insert('end', 'Ian')
    self.lb1.insert('end', 'Johnny')

def start_thread(self):
    for i in range(self.lb1.size()):
        t1 = threading.Thread(target = self.work, args=(i,))
        t1.start()

def work(self, i):
    print(self.lb1.get(i))

if __name__ == '__main__':
    app = MainWindow()
    app.title('Main Window')
    app.configure(bg='#333333')
    app.mainloop()

当前问题:线程执行顺序混乱,打印的名称顺序随机,甚至出现多个名称挤在同一行的情况。示例输出:

ChrisEdward
DavidBilly

George

Aaron
Frank
Ian
JohnnyHoward

解决方案

要实现线程并发执行但打印严格按Listbox顺序,核心是分离「并发的业务逻辑」和「有序的打印操作」,通过线程同步机制控制打印时机。以下是两种可行方案:

方案1:使用锁+计数器控制打印顺序

通过共享锁和计数器,让每个线程仅在轮到自己的序号时执行打印操作,其余时间等待。线程的业务逻辑可以并发执行,仅打印步骤被有序控制。

修改后的完整代码:

from tkinter import Tk, Button, Listbox
import threading
import time

class MainWindow(Tk):
    def __init__(self):
        super().__init__()

        self.lb1 = Listbox(self, width=26, cursor='hand2')
        self.lb1.pack(side='left', fill='y', padx=20, pady=20)

        self.b1 = Button(self, text='START', bg='green', fg='white', cursor='hand2', command=self.start_thread)
        self.b1.pack(side='left')

        # 批量插入名称,简化代码
        self.names = ['Aaron', 'Billy', 'Chris', 'David', 'Edward', 'Frank', 'George', 'Howard', 'Ian', 'Johnny']
        for name in self.names:
            self.lb1.insert('end', name)
        
        # 同步用的锁和计数器,初始计数器指向第一个待打印的索引
        self.print_lock = threading.Lock()
        self.current_print_index = 0

    def start_thread(self):
        threads = []
        for idx in range(self.lb1.size()):
            thread = threading.Thread(target=self.work, args=(idx,))
            threads.append(thread)
            thread.start()
        
        # 可选:等待所有线程执行完毕
        for thread in threads:
            thread.join()

    def work(self, idx):
        # ----------------------
        # 这里替换为你的并发业务逻辑
        # 比如耗时的计算、IO操作等
        time.sleep(0.1)
        # ----------------------

        # 进入打印同步控制
        with self.print_lock:
            # 循环等待,直到轮到当前线程的索引
            while self.current_print_index != idx:
                # 临时释放锁,让其他线程有机会检查计数器
                self.print_lock.release()
                time.sleep(0.01)
                self.print_lock.acquire()
            
            # 执行打印
            print(self.lb1.get(idx))
            # 计数器递增,允许下一个线程打印
            self.current_print_index += 1

if __name__ == '__main__':
    app = MainWindow()
    app.title('Main Window')
    app.configure(bg='#333333')
    app.mainloop()

方案2:使用信号量链式触发

为每个线程创建一个信号量,第一个线程的信号量初始释放,后续线程需等待前一个线程完成后释放自己的信号量,形成链式触发,保证打印顺序。

完整代码:

from tkinter import Tk, Button, Listbox
import threading
import time

class MainWindow(Tk):
    def __init__(self):
        super().__init__()

        self.lb1 = Listbox(self, width=26, cursor='hand2')
        self.lb1.pack(side='left', fill='y', padx=20, pady=20)

        self.b1 = Button(self, text='START', bg='green', fg='white', cursor='hand2', command=self.start_thread)
        self.b1.pack(side='left')

        self.names = ['Aaron', 'Billy', 'Chris', 'David', 'Edward', 'Frank', 'George', 'Howard', 'Ian', 'Johnny']
        for name in self.names:
            self.lb1.insert('end', name)
        
        # 创建信号量列表:第一个信号量初始允许通行,其余初始阻塞
        self.semaphores = [threading.Semaphore(0) for _ in self.names]
        self.semaphores[0].release()

    def start_thread(self):
        threads = []
        for idx in range(self.lb1.size()):
            thread = threading.Thread(target=self.work, args=(idx,))
            threads.append(thread)
            thread.start()
        
        for thread in threads:
            thread.join()

    def work(self, idx):
        # 等待当前线程的信号量
        self.semaphores[idx].acquire()
        
        # ----------------------
        # 并发执行的业务逻辑
        time.sleep(0.1)
        # ----------------------
        
        # 打印内容
        print(self.lb1.get(idx))
        
        # 如果不是最后一个线程,释放下一个线程的信号量
        if idx < len(self.semaphores) - 1:
            self.semaphores[idx + 1].release()

if __name__ == '__main__':
    app = MainWindow()
    app.title('Main Window')
    app.configure(bg='#333333')
    app.mainloop()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 23:35:26