为何Python带锁多线程比串行代码更快?相关疑问求解
问题解答
测试代码与结果
测试代码:
import threading import time global_v = 0 thread_lock = threading.Lock() def thread_test(num): thread_lock.acquire() global global_v for _ in range(num): global_v += 1 thread_lock.release() # thread run thread_1 = threading.Thread(target=thread_test, args=(9_000_000,)) thread_2 = threading.Thread(target=thread_test, args=(9_000_000,)) thread_3 = threading.Thread(target=thread_test, args=(9_000_000,)) thread_4 = threading.Thread(target=thread_test, args=(9_000_000,)) thread_5 = threading.Thread(target=thread_test, args=(9_000_000,)) thread_start = time.perf_counter() # start thread thread_1.start() thread_2.start() thread_3.start() thread_4.start() thread_5.start() thread_end = time.perf_counter() thread_1.join() thread_2.join() thread_3.join() thread_4.join() thread_5.join() print(f"multithread run takes {thread_end-thread_start:.5f} sec") # nomal run (sequential code) def increment(): global nomal_result for _ in range(45_000_000): nomal_result += 1 nomal_result = 0 start_time = time.perf_counter() increment() end_time = time.perf_counter() print(f"nomal run takes {end_time-start_time:.5f} sec")
测试结果:
multithread run takes 0.21226 sec nomal run takes 2.09347 sec
Q1:为何Python中多线程代码比串行代码更快?
核心原因是你的计时逻辑错误。
你统计的多线程耗时只记录了调用start()启动线程的时间,并没有统计线程实际执行任务的全部时长。thread_start在启动线程前记录,thread_end在所有start()调用完成后立刻记录,但此时线程还在后台运行,真正的执行结束节点应该是所有join()调用完成之后。
把thread_end = time.perf_counter()移到所有join()之后再测试,你会发现多线程的实际执行时间和串行代码接近甚至更慢——因为你的锁粒度太大,线程根本无法并行执行,本质是串行跑的,再加上线程切换的额外开销,总时间只会比串行更长。
Q2:带锁的多线程代码与串行代码有何区别?我原以为使用锁后代码会像串行代码一样阻塞执行。
你的理解是对的,还是计时错误导致了结果误导。
你在thread_test函数开头就获取锁,直到整个循环执行完毕才释放,这意味着同一时刻只有一个线程能执行循环逻辑,其他线程都在等待锁释放,完全是串行执行的逻辑。但你之前的计时只算了启动线程的时间,没算线程实际运行的时间,所以看起来多线程更快。
如果锁的粒度更小(比如把锁放在循环内部,每次global_v +=1前后加锁、释放),线程会频繁抢锁、释放锁,此时多线程的执行时间会比串行慢很多——因为GIL的存在,加上锁竞争的开销,线程切换的成本会被放大。而你当前的锁粒度覆盖整个循环,和串行代码的执行流程几乎一致,只是多了线程启动和切换的一点点额外开销。
内容的提问来源于stack exchange,提问作者jihyeon
相关产品推荐
相关产品推荐

