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

Python多线程存在print指令时线程未完成任务的原因分析

问题描述

我尝试在Python中并行化一个简单任务,编写的代码如下:

import threading


def function(dictionary):
    if threading.currentThread().name == "Thread 1":
        print("thread 1...")
        for i in sorted(dictionary)[0: int(len(dictionary) / 2)]:
            dictionary[i] = sum(dictionary[i])

    if threading.currentThread().name == "Thread 2":
        print("thread 2...")
        for i in sorted(dictionary)[int(len(dictionary) / 2):]:
            dictionary[i] = sum(dictionary[i])


dictionary = {}
dictionary["k1"] = [1, 1]
dictionary["k2"] = [1, 1, 1]
dictionary["k3"] = [1, 1, 1, 1]
dictionary["k4"] = [1]
dictionary["k5"] = [1, 1, 1]
dictionary["k6"] = [1, 1]
dictionary["k7"] = [1, 1, 1]
dictionary["k8"] = [1, 1, 1, 1, 1]


t1 = threading.Thread(name="Thread 1", target=function, args=(dictionary,))
t2 = threading.Thread(name="Thread 2", target=function, args=(dictionary,))

t1.start()
t2.start()

print(dictionary)

实际运行输出结果为:

thread 1...
thread 2...
{'k1': 2, 'k2': 3, 'k3': 4, 'k4': 1, 'k5': [1, 1, 1], 'k6': [1, 1], 'k7': [1, 1, 1], 'k8': [1, 1, 1, 1, 1]}

预期输出结果应为:

{'k1': 2, 'k2': 3, 'k3': 4, 'k4': 1, 'k5': 3, 'k6': 2, 'k7': 3, 'k8': 5}

目前只有移除代码中的print指令时,才能正常得到预期结果,请问该现象的产生原因是什么?


问题根源

这个现象和print本身没有任何本质关系,核心原因是主线程根本没等两个子线程跑完任务,就直接读取并打印了共享字典。代码里的print语句只是改变了线程调度的时序,才让你误以为“移除print就能正常运行”。
具体的执行逻辑拆解:

  • 你在调用t1.start()、t2.start()启动两个子线程后,没有搭配join()方法阻塞等待子线程执行结束,主线程启动完子线程就会立刻往下执行print(dictionary)的逻辑。
  • Python的多线程执行依赖GIL(全局解释锁)轮流分配执行权,控制台打印这类IO操作会主动触发GIL释放,让操作系统重新调度线程的执行顺序:
    • 当子线程代码里保留print时,子线程执行打印操作会主动释放GIL,此时主线程很容易抢到执行权,赶在Thread 2开始循环处理后半段字典键值的求和逻辑前,就直接读取了字典内容。这也是你看到字典后半段的值还是原始列表、没被替换成求和结果的直接原因。
    • 当你把子线程里的print删掉后,两个子线程从启动到跑完字典处理循环的耗时极短,主线程还没来得及抢到执行权执行打印逻辑,两个子线程就已经完成了全部字典更新,此时才会看起来“得到了预期结果”。
  • 本质上这是典型的线程竞态问题:主线程和两个子线程并发读写同一个共享字典,没有加任何同步逻辑,最终打印的字典状态完全取决于CPU随机调度的顺序。就算你删掉所有print,只要运行次数足够多,依然会遇到主线程抢跑、打印出还没处理完成的字典的情况。

修复方式

在主线程打印字典前,显式等待两个子线程执行完成,在t2.start()的代码后面追加两行:

t1.join()
t2.join()

加完后主线程会一直阻塞,直到Thread 1和Thread 2全部跑完字典的求和逻辑,再执行打印操作,就能稳定得到预期的结果。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.30 09:06:01