异步过程调用

Bit*_*lue 6 c c++ winapi multithreading asynchronous

我试图让APC在我的C++代码中工作,但我失败了.我迷失了语言.在C#中它可以正常工作(逻辑上相同的代码).我希望线程2在线程1中注入一个调用.但在我的C++项目中,由于某种原因它不会执行.我究竟做错了什么 ?

  • 线程1(主线程)
  • 线程2(子线程,需要主线程来执行一个函数)


码:

#include "stdio.h"
#include "windows.h"

#define TIME 2500
#define LAST_ERROR printf("last error: %i\r\n", GetLastError());

HANDLE handle1, handle2;

void ThreadInfo(char* prefix = "")
{
    printf("%sthread id: %i\r\n", prefix, GetCurrentThreadId());
}

VOID CALLBACK apc( _In_ ULONG_PTR data)
{
    ThreadInfo(" -> apc: 2 -> 1: ");
}

void run1()
{
    while (true)
    {
        Sleep(TIME);
        ThreadInfo("1: ");

        // apc
        //QueueUserAPC(apc, handle2, (ULONG_PTR) NULL);
    }
}

void run2()
{
    while (true)
    {
        Sleep(TIME);
        ThreadInfo("2: ");

        // apc
        QueueUserAPC(apc, handle1, (ULONG_PTR) NULL);
    }
}

void TestThreads()
{
    DWORD threadId;
    SECURITY_ATTRIBUTES a;
    a.nLength = 12;
    a.lpSecurityDescriptor = NULL;
    a.bInheritHandle = 1;

    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &handle1, 0, true, 2);
    LAST_ERROR

    handle2 = CreateThread(NULL, 100000, (LPTHREAD_START_ROUTINE)run2, NULL, 0, &threadId);
    printf("handles (1, 2): %i, %i\r\n", handle1, handle2);
    printf("ids (1, 2): %i, %i\r\n", threadId, GetCurrentThreadId());
    printf("--------------------------------\r\n");
    run1();
}

int main()
{
    TestThreads();

    printf("done.");
    getchar();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 10

  Sleep(TIME);
Run Code Online (Sandbox Code Playgroud)

这是你的问题陈述.APC非常危险,它们允许代码重新进入.臭名昭着的相当于臭名昭着的Application.DoEvents()语句让很多VB程序员陷入困境.Windows不只是让它们运行,您必须明确表示您的代码是可重入的,以便APC可以安全地运行而不会搞砸您的程序状态.

具体要求是您的线程处于"可警告的等待状态".进入等待状态不是问题,Sleep()调用会这样做.然而,这不是一个可警告的状态.你必须改用它:

  SleepEx(TIME, TRUE);
Run Code Online (Sandbox Code Playgroud)

修改测试程序中的run1()函数,您现在将看到调用APC回调.与GetOverlappedResultEx(),SignalObjectAndWait(),WaitForSingleObjectEx()和WaitForMultipleObjectsEx()相比,其他winapi调用可以使线程处于可警告的等待状态.是的,托管程序中的Thread.Sleep()是可警告的,CLR会调用SleepEx().

  • 我是asleepex :)我对你的repro代码的赞美,希望每个SO用户都能像你一样轻松地帮助他们. (3认同)