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

Python 3线程与Python 2性能差异及上下文切换问题咨询

Why Your Python 3 Threaded Code Has Unexpected Delays (And How to Fix It)

Let’s break down what’s going on here, and why you’re seeing such a stark difference between Python 2.7 and Python 3.6.

The Core Issue: GIL Scheduling Changes

The root cause lies in how Python’s Global Interpreter Lock (GIL) works across versions. The GIL ensures only one Python thread executes bytecode at a time, but its scheduling logic changed significantly after Python 2.7:

  • Python 2.7: The GIL was released after every 100 bytecode instructions. Even if your main thread was stuck in a while ... pass busy-wait, it would regularly drop the GIL, letting your thread_it thread jump in and update the flag quickly. That’s why you saw consistent ~15ms runtimes.
  • Python 3.2+: The GIL switched to a time-based scheduling system. By default, a thread holds the GIL for up to 5ms before being forced to release it. But tight CPU-bound loops like while ... pass can bypass this check — your main thread ends up hogging the GIL, preventing thread_it from getting a chance to set the flag, hence the 10ms delay you’re seeing.

Why sleep(0) or print() Fixes It

When you add systime.sleep(0) or print("") to those while loops, you’re forcing the thread to give up the GIL immediately:

  • sleep(0): Even though it doesn’t pause execution, this call tells the OS scheduler the thread is willing to yield CPU time. In Python, this triggers an explicit GIL release, letting other threads run.
  • print(""): Any I/O operation (like writing to stdout) causes the GIL to be released, since Python has to wait for the OS to handle the I/O. This gives your thread_it thread a window to update the flag.

Moving these operations to an external function doesn’t help because the busy-wait loop itself is still running in the main thread, holding onto the GIL until that external function call triggers a release.

The Better Fix: Ditch Busy-Waits for Proper Synchronization

Busy-waiting with while ... pass is almost never the right approach for thread synchronization. Python’s threading module has built-in tools designed for exactly this kind of signaling — like threading.Event().

Here’s a revised version of your code using Event instead of manual flag checks:

# -*- coding: utf-8 -*-
import time as systime
import timeit
import threading

thread_flag = False
counter = 0
AVERAGE = 10
# Use an Event for thread-safe signaling
flag_event = threading.Event()

def thread_it():
    global thread_flag
    while not thread_flag:
        # Wait until the event is cleared (instead of looping on a flag)
        flag_event.wait()
        systime.sleep(0.05)
        # Signal the main thread we're ready
        flag_event.set()

def to_time_it():
    global counter
    # Clear the event to let the thread proceed
    flag_event.clear()
    systime.sleep(0.03)
    # Wait for the thread to signal us (no busy-wait!)
    flag_event.wait()
    print("OK: %s" % counter)
    # Reset for the next cycle
    flag_event.clear()
    counter += 1

def main():
    global thread_flag
    print("Threading while loop test")
    th = threading.Thread(target=thread_it)
    th.daemon = True  # Let the thread exit when the main program does
    th.start()
    tajm = timeit.timeit(to_time_it, number=AVERAGE) * 1000 / AVERAGE
    systime.sleep(0.7)
    # Shutdown the thread
    thread_flag = True
    flag_event.set()  # Wake the thread so it can exit the loop
    print("\n\tAverage time: %.2f ms" % tajm)

if __name__ == '__main__':
    main()

This code runs consistently fast across Python 2 and 3 because:

  • Event.wait() puts the thread into a blocked state instead of spinning on the CPU. It doesn’t hold the GIL, so other threads can run freely.
  • Signaling between threads is handled at the OS level, which is far more efficient and reliable than manual flag polling.

Key Takeaways

  • Python 3’s GIL scheduling is more efficient for most workloads, but it penalizes tight CPU-bound busy-waits.
  • sleep(0) and I/O operations force GIL releases, but they’re workarounds, not proper solutions.
  • Always use Python’s built-in synchronization primitives (Event, Condition, Lock) for thread communication — they’re designed to avoid these exact problems.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:47:47