为什么Python守护线程内的while True循环会阻塞主代码执行?
阻塞问题核心成因
- 最根本的启动逻辑错误:完全没有真正启动子线程
在自定义Mysql类的__init__方法末尾,直接写入了self.run()调用。这是普通实例方法的同步执行,整个过程都跑在主线程的执行栈上,没有触发Python threading模块的新线程创建流程。
threading.Thread类的正确启动方式是调用实例的.start()方法:这个方法才会和操作系统交互创建独立执行流,让后台新线程去执行run()中定义的逻辑。直接调用run()的写法和调用普通成员方法没有任何区别,while True循环会直接把主线程卡死在该方法内部,代码永远走不到data = Mysql()之后的逻辑,这就是“程序无法越过循环继续执行”的直接原因。 - 已设置的daemon守护线程属性完全失效
守护线程的标记仅对被.start()方法拉起的独立系统线程生效,当前所有代码都运行在主线程中,该属性不会对实际执行流产生任何作用。 - 即使修正了启动方式,现有代码还存在两处会导致运行异常的隐患:
- 共享同一MySQL游标
mycursor的线程安全问题:mysql-connector-python的游标对象并非线程安全,多个线程同时调用同一个游标执行查询,会出现游标状态错乱、返回结果不匹配、甚至数据库连接被强制断开的问题。正确做法是为每个线程创建独立的数据库连接和游标,或为所有数据库操作加全局互斥锁。 - 无休眠的忙循环消耗资源:
while True数据库读取循环没有加入任何休眠逻辑,会将单个CPU核心占满,无意义地消耗系统资源,应在每轮循环后加入短休眠(如0.1秒),降低数据库查询频率。
- 共享同一MySQL游标
修正后的核心逻辑参考
class Mysql(threading.Thread): def __init__(self): super().__init__() self.daemon = True self.en = 1 self.læsTidScoreNu = "SELECT tidSCoreNu FROM bois WHERE id = 1" self.læsTidMål = "SELECT tidMål FROM bois WHERE id = 1" self.opdaterTidMål = "UPDATE bois SET tidMål = %s WHERE id = %s" self.læsStopKnap = "SELECT stopKnap FROM bois WHERE id = 1" # 为子线程创建独立数据库连接,避免和主线程共享游标 self.thread_db = mysql.connector.connect( host="", user="", password="", database='', auth_plugin='' ) self.thread_cursor = self.thread_db.cursor(prepared=True) def run(self) -> None: # 先执行一轮查询,给状态变量赋初始值,避免主线程首次读取时出现属性不存在的错误 self.thread_cursor.execute(self.læsTidScoreNu) self.råTidScoreNu = self.thread_cursor.fetchone() self.tidScoreNu = float("{}".format('%.2f' % self.råTidScoreNu)) self.thread_cursor.execute(self.læsTidMål) self.råTidMål = self.thread_cursor.fetchone() self.tidMål = float("{}".format('%.2f' % self.råTidMål)) self.thread_cursor.execute(self.læsStopKnap) self.stopKnap = self.thread_cursor.fetchone()[0] while True: self.thread_cursor.execute(self.læsTidScoreNu) self.råTidScoreNu = self.thread_cursor.fetchone() self.tidScoreNu = float("{}".format('%.2f' % self.råTidScoreNu)) self.thread_cursor.execute(self.læsTidMål) self.råTidMål = self.thread_cursor.fetchone() self.tidMål = float("{}".format('%.2f' % self.råTidMål)) self.thread_cursor.execute(self.læsStopKnap) self.stopKnap = self.thread_cursor.fetchone()[0] # 短休眠降低CPU占用 sleep(0.1) # 正确启动线程:调用start(),而非直接调用run() data = Mysql() data.start() # 等待子线程完成首轮初始化赋值,避免主线程读取到未初始化的变量 sleep(0.5)
额外注意:原代码中读取
stopKnap时,fetchone()返回的是元组类型,直接拿元组和整数1做判断永远不会成立,需要取元组的第一个元素再做比较。
内容的提问来源于stack exchange,提问作者GeorgeWTrump
相关产品推荐
相关产品推荐

