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

Python多线程间非布尔变量共享问题(Vosk音频播放器场景)

问题描述

我正在使用Vosk开发一款音频播放器,已编写两个函数:一个用于播放音乐(playMusic),另一个用于监听用户语音输入(checkwhileplaying)。具体代码如下:

keywords = ['keyword', 'keyword', 'keyword', 'keyword', 'keyword']

stop = False

def playMusic(music): #This one plays the music while 'stop' is false
    while audio.is_set():
        music.play()
        time.sleep(music.get_length())
        break
    return

def checkwhileplaying(): #This one checks for any new messages from the user
    audio.set()
    data = q.get()
    if rec.AcceptWaveform(data):
        time.sleep(1)
        if rec.Result()[2] == 't': #This puts the output as one simple sentence instead of 
            message = rec.Result()[14:-3]
        else:
            message = rec.Result()[17:-3]
    else:
        if rec.PartialResult()[2] == 't':
            time.sleep(1)
            message = rec.PartialResult()[14:-3]
        else:
            message = rec.PartialResult()[17:-3]
    for x in keywords:
        if x in message:
            music = ('filepath/filepath/file.wav')
    if 'stop' in stopmessage:
        audio.clear()
    
play = threading.Thread(target=playMusic,args=(stop,), daemon=True)
audio = threading.Event()
check = threading.Thread(target=checkwhileplaying, daemon=True)
audio.set()
play.start()
check.start()

我的设计思路是:监听函数在监听过程中,若检测到关键词,就设置音乐文件路径,供播放函数调用播放。但我是Python新手,不了解全局变量、静态变量等概念,请问如何在这两个线程间共享非布尔变量?


解决方案

在Python多线程场景下,共享非布尔变量可以用以下几种简单实用的方法:

1. 使用全局变量

直接在函数外部定义需要共享的变量,在函数内部用global关键字声明要修改这个全局变量,就能实现线程间的数据传递。针对你的需求,示例代码如下:

# 定义全局变量存储当前要播放的音乐路径
current_music_path = None

keywords = ['keyword', 'keyword', 'keyword', 'keyword', 'keyword']
stop = False
audio = threading.Event()

def playMusic():
    global current_music_path
    while audio.is_set():
        # 检测到有新的音乐路径时播放
        if current_music_path is not None:
            # 这里根据你实际使用的音频库初始化音乐对象,示例用pygame.mixer
            music = pygame.mixer.Sound(current_music_path)
            music.play()
            time.sleep(music.get_length())
            # 播放完成后重置路径,避免重复播放同一首
            current_music_path = None
        # 加短延时降低CPU占用
        time.sleep(0.1)

def checkwhileplaying():
    global current_music_path
    audio.set()
    data = q.get()
    if rec.AcceptWaveform(data):
        time.sleep(1)
        if rec.Result()[2] == 't':
            message = rec.Result()[14:-3]
        else:
            message = rec.Result()[17:-3]
    else:
        if rec.PartialResult()[2] == 't':
            time.sleep(1)
            message = rec.PartialResult()[14:-3]
        else:
            message = rec.PartialResult()[17:-3]
    
    for x in keywords:
        if x in message:
            # 修改全局变量,传递音乐路径
            current_music_path = 'filepath/filepath/file.wav'
    
    # 原代码的stopmessage未定义,这里修正为message
    if 'stop' in message:
        audio.clear()

# 启动线程
play = threading.Thread(target=playMusic, daemon=True)
check = threading.Thread(target=checkwhileplaying, daemon=True)
audio.set()
play.start()
check.start()

注意:如果是对字典、列表这类复杂数据结构同时读写,需要加threading.Lock避免数据混乱,但你这里只是简单赋值字符串,暂时不需要锁。

2. 使用线程安全队列(queue.Queue)

如果需要传递多个音乐路径,或者不想用全局变量,可以用queue.Queue来传递数据。监听函数将路径放入队列,播放函数从队列中取出,队列本身自带线程安全机制,无需额外加锁:

import queue

# 创建线程安全的队列,用于存放待播放的音乐路径
music_queue = queue.Queue()

keywords = ['keyword', 'keyword', 'keyword', 'keyword', 'keyword']
stop = False
audio = threading.Event()

def playMusic():
    while audio.is_set():
        try:
            # 非阻塞方式取队列元素,避免线程卡住
            music_path = music_queue.get(block=False)
            music = pygame.mixer.Sound(music_path)
            music.play()
            time.sleep(music.get_length())
        except queue.Empty:
            # 队列为空时短暂休眠
            time.sleep(0.1)

def checkwhileplaying():
    audio.set()
    data = q.get()
    if rec.AcceptWaveform(data):
        time.sleep(1)
        if rec.Result()[2] == 't':
            message = rec.Result()[14:-3]
        else:
            message = rec.Result()[17:-3]
    else:
        if rec.PartialResult()[2] == 't':
            time.sleep(1)
            message = rec.PartialResult()[14:-3]
        else:
            message = rec.PartialResult()[17:-3]
    
    for x in keywords:
        if x in message:
            # 将音乐路径放入队列
            music_queue.put('filepath/filepath/file.wav')
    
    if 'stop' in message:
        audio.clear()

# 启动线程
play = threading.Thread(target=playMusic, daemon=True)
check = threading.Thread(target=checkwhileplaying, daemon=True)
audio.set()
play.start()
check.start()

3. 使用自定义类封装状态

如果后续功能会扩展,用类封装共享状态和方法会让代码更清晰易维护,避免全局变量分散的问题:

class AudioPlayer:
    def __init__(self):
        self.current_music_path = None
        self.audio_event = threading.Event()
        self.keywords = ['keyword', 'keyword', 'keyword', 'keyword', 'keyword']
    
    def playMusic(self):
        while self.audio_event.is_set():
            if self.current_music_path is not None:
                music = pygame.mixer.Sound(self.current_music_path)
                music.play()
                time.sleep(music.get_length())
                self.current_music_path = None
            time.sleep(0.1)
    
    def checkwhileplaying(self):
        self.audio_event.set()
        data = q.get()
        if rec.AcceptWaveform(data):
            time.sleep(1)
            if rec.Result()[2] == 't':
                message = rec.Result()[14:-3]
            else:
                message = rec.Result()[17:-3]
        else:
            if rec.PartialResult()[2] == 't':
                time.sleep(1)
                message = rec.PartialResult()[14:-3]
            else:
                message = rec.PartialResult()[17:-3]
        
        for x in self.keywords:
            if x in message:
                self.current_music_path = 'filepath/filepath/file.wav'
        
        if 'stop' in message:
            self.audio_event.clear()

# 实例化播放器并启动线程
player = AudioPlayer()
play = threading.Thread(target=player.playMusic, daemon=True)
check = threading.Thread(target=player.checkwhileplaying, daemon=True)
player.audio_event.set()
play.start()
check.start()

额外小提示

原代码里还有几个需要修正的细节:

  • playMusic函数参数是music,但启动线程时传的是stop,参数不匹配,要修正;
  • stopmessage变量未定义,应该是message;
  • playMusic里的while循环执行一次就break,无法持续监听音乐路径变化,需要去掉break。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 21:56:02