多线程计算圆周率(π)时输出结果随机异常问题排查
问题排查与修复
问题根源
你的代码结果随机的核心原因是线程执行顺序不确定:
- 多个线程向全局列表
io中追加计算结果的顺序完全由操作系统线程调度决定,没有固定性 - 后续汇总时,你通过
io列表的索引奇偶性来决定加减符号,这完全依赖列表元素的顺序与项的符号严格对应,但线程顺序随机导致符号匹配完全混乱,最终结果偏离π
修复思路
不要依赖线程执行顺序来判断符号,而是将每个线程计算的项对应的符号与数值绑定存储,确保汇总时符号与对应项的和一一对应,不受线程顺序影响。
莱布尼茨公式中,项的符号由分母决定:
π/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - ...
即分母为4k+1时符号为正,4k+3时符号为负,也可以通过(-1)^((分母-1)/2)计算符号。
修复后的代码
from decimal import * import threading from threading import Thread as thr getcontext().prec = 600 io = [] def ev(startingVal, rangeValue, countThread): global io # 计算当前分组项的符号 term_index = (startingVal - 1) // 2 sign = (-1) ** term_index number = Decimal(0) for x in range(rangeValue): denominator = startingVal + countThread * x number += Decimal(1) / denominator # 存储符号与对应项的和 io.append((sign, number)) thread_count = 2 * 8 # 控制线程分组间隔,8个实际工作线程 def listThread(): # 生成处理所有奇数分母分组的线程 return [thr(target=ev, args=(x, 10000, thread_count)) for x in [y for y in range(thread_count) if y % 2 == 1]] rt = listThread() for x in rt: x.start() for x in rt: x.join() # 汇总时按绑定的符号计算 k = Decimal(0) for sign, val in io: k += sign * val print(k * 4)
额外优化点
- 移除了原代码中未使用的
n、num变量,精简代码 - 去掉了原
ev函数中无用的oldNum参数,每个线程独立计算对应分组的和
内容的提问来源于stack exchange,提问作者human dude
相关产品推荐
相关产品推荐

