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
相关产品推荐
相关产品推荐

