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

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

  1. Output Interleaving: The print function isn't atomic in Python. Even with end='\n', constructing the string and writing to stdout are separate operations—another thread can interrupt between these steps, causing mixed output.
  2. 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_pressed is 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_ready and print_ready events force each thread to wait for the other to finish before running again. This ensures incA runs once, then printA, then incA, and so on—exactly the alternating behavior you want.
  • Atomic Print: The print_lock wraps each print operation to ensure it completes without interruption, eliminating interleaved output. We also added flush=True to 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 16:13:12