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

如何用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()播放声音,此时该线程会被阻塞,但其他线程仍能正常运行,不会影响其他弦的检测与触发

注意事项

  1. 线程安全:共享变量(如extended数组、finger.is_extended)需要用threading.Lock保护,避免多个线程同时读写导致数据错乱
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:18:21