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

Win32 QueueUserAPC函数随机报错31及执行不稳定问题求助

问题分析与修复方案

核心问题根因

1. 调度线程逻辑完全写反(导致偶发少输出、错误码31的核心原因)

你写的pure调度线程的等待逻辑完全颠倒:
原逻辑是只要WaitForSingleObjectEx返回WAIT_IO_COMPLETION(即APC投递执行完成)就直接退出线程,所以第一个APC执行完调度线程就终止了,后续所有APC投递都会发往已经退出的线程,自然就会出现thread opened输出少、第二次QueueUserAPC返回错误码31(ERROR_GEN_FAILURE,代表向已终止/不可用的线程投递APC)。

2. 事件触发时序存在竞速条件

你在唤醒工作线程的SetEvent(g_FenceEvent)执行后,立刻触发了调度线程的退出事件dispatchCloseEvent,导致部分工作线程还没来得及完成第二次QueueUserAPC投递,调度线程就已经进入退出流程,进一步拉高了错误概率。

3. 无效重复触发事件

代码末尾的SetEvent(dispatchCloseEvent)是多余操作,dispatchCloseEvent是手动重置事件,触发一次就会永久保持信号状态,不需要重复设置。


修复代码

#include <Windows.h>
#include <iostream>

constexpr auto fenceName = L"GlobalFence";
constexpr auto dispatchCloser = L"GlobalDispatchStop";
constexpr int threadCount = 5;

DWORD WINAPI pure(LPVOID lpThreadParameter)
{
    const HANDLE dispatchCloseEvent = OpenEventW(EVENT_ALL_ACCESS, FALSE, dispatchCloser);
    // 修正等待逻辑:只有等到退出事件触发才退出,APC执行完成后继续等待
    while (true) {
        DWORD waitRet = WaitForSingleObjectEx(dispatchCloseEvent, INFINITE, TRUE);
        if (waitRet == WAIT_OBJECT_0) {
            // 收到退出信号,结束线程
            break;
        }
        // WAIT_IO_COMPLETION说明APC执行完成,继续循环等待下一个事件
    }
    CloseHandle(dispatchCloseEvent);
    return 0;
}

HANDLE dispatcher;
int main()
{
    const HANDLE dispatchCloseEvent = CreateEventW(nullptr, TRUE, FALSE, dispatchCloser);
    dispatcher = CreateThread(NULL, 1024, &pure, 0, 0, NULL);
    const HANDLE g_FenceEvent = CreateEventW(nullptr, TRUE, FALSE, fenceName);
    HANDLE threads[threadCount];
    for (int i = 0; i < threadCount; i++)
    {
        threads[i] = CreateThread(NULL, 1024*1024, 
            [](LPVOID) -> DWORD 
            {
                DWORD d = QueueUserAPC((PAPCFUNC)[](ULONG_PTR) {std::cout << "thread opened\n"; }, dispatcher, NULL);
                if(d == 0)std::cout << GetLastError() << std::endl;

                HANDLE a = OpenEventW(EVENT_ALL_ACCESS, FALSE, fenceName);
                WaitForSingleObject(a, INFINITE);
                CloseHandle(a);

                d = QueueUserAPC((PAPCFUNC)[](ULONG_PTR) {std::cout << "thread released\n"; }, dispatcher, NULL);
                if (d == 0)std::cout << GetLastError() << std::endl;

                return 0;
            }, 
            0, 0, NULL);
    }

    Beep(300, 300);

    SetEvent(g_FenceEvent);
    // 先等所有工作线程完成全部APC投递,再触发调度线程退出
    WaitForMultipleObjects(threadCount, threads, TRUE, INFINITE);
    SetEvent(dispatchCloseEvent);
    WaitForSingleObject(dispatcher, INFINITE);

    for (int i = 0; i < threadCount; i++)
        CloseHandle(threads[i]);

    CloseHandle(dispatcher);
    CloseHandle(g_FenceEvent);
    CloseHandle(dispatchCloseEvent);
}

修复说明

  • 修正了调度线程的等待逻辑,只有收到明确的退出信号才会终止,所有投递的APC都会被正常执行
  • 调整了退出事件的触发时机:先等待所有工作线程执行完毕(完成全部APC投递),再通知调度线程退出,完全规避竞速问题
  • 补充了句柄关闭操作,避免资源泄漏
  • 显式将无捕获lambda转换为PAPCFUNC类型,避免部分编译器出现类型匹配错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 13:24:03