ThreadPoolExecutor线程饥饿与打印输出截断问题求助
Solution to Thread Output Interleaving and Starvation Issues
Let's break down the problems you're facing and fix them step by step:
Why the Original Issues Happen
- Output Interleaving: The
printfunction isn't atomic in Python. Even withend='\n', constructing the string and writing to stdout are separate operations—another thread can interrupt between these steps, causing mixed output. - Thread Starvation: When you wrapped the entire loop body in a lock, you let one thread hold the lock for long stretches (since
keyboard.is_pressedis fast, a thread might keep re-acquiring the lock before the other gets a chance). Plus, there's no mechanism to enforce alternation between the two threads.
Fix: Synchronized Alternation with Atomic Output
We'll use two threading.Event objects to enforce strict alternation between incA and printA, and a lock to make sure print operations are atomic. Here's the revised code:
from concurrent.futures import ThreadPoolExecutor, as_completed import threading import keyboard a = 0 # Events to signal when each thread can run inc_ready = threading.Event() print_ready = threading.Event() # Lock for atomic print operations print_lock = threading.Lock() def incA(): global a # Let printA start first (optional, adjust based on your preference) inc_ready.set() while True: # Wait until printA has finished its turn inc_ready.wait() inc_ready.clear() # Increment a (this is atomic for ints in Python, no lock needed here) new_a = a + 1 with print_lock: print(f'inc a: {a} -> {new_a}', end='\n', flush=True) a = new_a # Signal printA to run print_ready.set() # Check for exit condition outside the critical section if keyboard.is_pressed('q'): # Make sure to signal the other thread before exiting print_ready.set() break def printA(): global a while True: # Wait until incA has finished incrementing print_ready.wait() print_ready.clear() # Use lock to ensure print is atomic with print_lock: print(f'print a: {a}', end='\n', flush=True) # Signal incA to run again inc_ready.set() # Check for exit condition outside the critical section if keyboard.is_pressed('q'): # Signal the other thread before exiting inc_ready.set() break with ThreadPoolExecutor(max_workers=2) as executor: f1 = executor.submit(incA) f2 = executor.submit(printA)
Key Improvements Explained
- Strict Alternation: The
inc_readyandprint_readyevents force each thread to wait for the other to finish before running again. This ensuresincAruns once, thenprintA, thenincA, and so on—exactly the alternating behavior you want. - Atomic Print: The
print_lockwraps eachprintoperation to ensure it completes without interruption, eliminating interleaved output. We also addedflush=Trueto make sure output is written immediately to stdout (buffering can cause unexpected behavior in multi-threaded setups). - Avoid Starvation: By using events instead of relying on lock contention, we guarantee each thread gets a turn. The exit check is moved outside the critical section so threads don't hold locks while waiting for user input.
Optional Adjustment
If you want incA to run first instead of printA, just swap the initial event setup—call print_ready.set() at the start of incA instead of inc_ready.set().
内容的提问来源于stack exchange,提问作者romhayh
相关产品推荐
相关产品推荐

