如何用Multithreading实现多虚拟弦同时拨奏与声音播放
虚拟弦项目多线程解决方案
测试代码问题分析与修复
你的测试代码中多线程调用input()导致输入逻辑混乱——多个线程共享标准输入流,输入内容会被拆分到不同的input()调用中,从而触发不了判断条件。比如你输入的1被第一个线程读取,但第二个线程的input()会继续等待输入,后续输入的字符被其他线程瓜分,导致每个线程拿到的输入不符合预期。
修复测试代码的思路是用单线程处理输入,通过事件触发对应线程的逻辑,避免多线程抢占输入流:
import threading def f1(event): event.wait() print("hello") def f2(event): event.wait() print("hello b") def f3(event): event.wait() print("hello c") # 创建事件对象用于触发线程逻辑 event1 = threading.Event() event2 = threading.Event() event3 = threading.Event() # 启动线程 Thread1 = threading.Thread(target=f1, args=(event1,)) Thread2 = threading.Thread(target=f2, args=(event2,)) Thread3 = threading.Thread(target=f3, args=(event3,)) Thread1.start() Thread2.start() Thread3.start() # 单线程处理输入,触发对应事件 val = input("enter number: ") if val == '1': event1.set() elif val == '2': event2.set() elif val == '3': event3.set() # 等待线程结束 Thread1.join() Thread2.join() Thread3.join() print("doneeeee")
多线程在虚拟弦项目中的适用性
完全适用!你当前的核心问题是playsound()是阻塞函数,播放声音时会卡住当前线程,导致无法同时检测其他弦的拨弦动作。将每个弦的检测与播放逻辑放到独立线程中,就能实现并行处理:
- 每个线程独立循环检测对应弦的拨弦条件(手指空间位置、伸展状态等)
- 当检测到拨弦动作时,调用
playsound()播放声音,此时该线程会被阻塞,但其他线程仍能正常运行,不会影响其他弦的检测与触发
注意事项
- 线程安全:共享变量(如
extended数组、finger.is_extended)需要用threading.Lock保护,避免多个线程同时读写导致数据错乱 - Arduino通信:如果多个线程需要向Arduino发送指令,也要加锁保护串口操作,防止数据冲突
实际弦函数优化示例
import threading import time from playsound import playsound # 共享变量与线程锁 extended = [0, 0, 0] shared_lock = threading.Lock() def string1(RT1, R1, finger): while True: # 加锁读取共享变量,保证线程安全 with shared_lock: current_extended = extended[0] is_finger_extended = finger.is_extended if RT1 <= R1: if is_finger_extended: with shared_lock: extended[0] = 1 print("pluck") if not is_finger_extended and current_extended == 1: with shared_lock: extended[0] = 0 print(RT1) print("String 1 has been plucked") # 播放声音,当前线程阻塞但不影响其他线程 playsound('/Desktop/soundG.mp3') # 此处添加Arduino控制逻辑(如移动弦) else: print("whomp whomp whomp 1") # 降低CPU占用率 time.sleep(0.01) # 同理定义string2、string3,修改对应索引、判断条件和声音文件 def string2(RT2, R2, finger): while True: with shared_lock: current_extended = extended[1] is_finger_extended = finger.is_extended if RT2 <= R2: if is_finger_extended: with shared_lock: extended[1] = 1 print("pluck string2") if not is_finger_extended and current_extended == 1: with shared_lock: extended[1] = 0 print(RT2) print("String 2 has been plucked") playsound('/Desktop/soundA.mp3') else: print("whomp whomp whomp 2") time.sleep(0.01)
线程启动方式
将线程设为后台守护线程,主线程保持运行即可:
# 假设RT1、R1、finger等变量已定义 thread1 = threading.Thread(target=string1, args=(RT1, R1, finger), daemon=True) thread2 = threading.Thread(target=string2, args=(RT2, R2, finger), daemon=True) # thread3 = ... thread1.start() thread2.start() # thread3.start() # 主线程持续运行,等待用户中断 try: while True: time.sleep(1) except KeyboardInterrupt: print("Exiting...")
内容的提问来源于stack exchange,提问作者souren.p
相关产品推荐
相关产品推荐

