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

