Win32事件 - 我需要在事件清除时收到通知

时间:2016-06-12 02:32:15

标签: multithreading winapi

Win 7,x64,Visual Studio Community 2015,C ++

我有一个我需要暂停/取消暂停或终止的线程,我目前使用手动重置"运行"或者"杀死"事件。线程中的循环每次暂停5000ms。

我的目标是能够在等待的中途停止等待或终止线程。

问题是我目前的设置方式,我需要在"运行"事件进入非信号状态,但没有办法做到这一点,除非我创建一个极性反转的事件,但这似乎是一个kludge。简而言之,我需要一个电平敏感信号,而不是边缘敏感信号。

也许事件应该只是切换运行状态?

这是线程函数:

DWORD WINAPI DAQ::_fakeOutFn(void *param) {
    DAQ *pThis = (DAQ *)param;
    const DWORD timeout = 5000;

    bool running = false;
    HANDLE handles[] = { pThis->hFakeTaskRunningEvent, pThis->hFakeTaskKillEvent };

    do {
        DWORD result = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
        switch (result) {
        case WAIT_OBJECT_0: // Run started or continued
            running = true;
            pThis->outputIndex++;
            if (pThis->outputIndex >= pThis->numSamples)
                pThis->outputIndex = 0;
            // Wait here
            // Not sure how to cancel this if the TaskRunningEvent goes false during the wait
            DWORD result2 = WaitForMultipleObjects(2, handles, FALSE, timeout);
            // Check result2, and 'continue' the loop if hFakeTaskRunningEvent went to NON-SIGNALLED state
            break;
        case WAIT_OBJECT_0 + 1: // Kill requested
            running = false;
            break;
        default:
            _ASSERT_EXPR(FALSE, L"Wait error");
            break;
        }
    } while (running);
    return 0;
}

2 个答案:

答案 0 :(得分:0)

为运行和恢复状态使用单独的事件。然后,您可以重置resume事件以暂停,并发出恢复事件的信号。应该使用running事件让线程知道什么时候有工作要做,而不是什么时候应该暂停该工作一段时间。

DWORD WINAPI DAQ::_fakeOutFn(void *param)
{
    DAQ *pThis = (DAQ *)param;

    bool running = false;
    HANDLE handles[] = { pThis->hFakeTaskRunningEvent, pThis->hFakeTaskKillEvent };

    do
    {
        DWORD result = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
        switch (result)
        {
            case WAIT_OBJECT_0: // Run started
            {
                running = true;
                pThis->outputIndex++;
                if (pThis->outputIndex >= pThis->numSamples)
                    pThis->outputIndex = 0;

                // check for pause here
                HANDLE handles2[] = { pThis->hFakeTaskResumeEvent, pThis->hFakeTaskKillEvent };
                DWORD result2 = WaitForMultipleObjects(2, handles2, FALSE, INFINITE);
                switch (result2)
                {
                    case WAIT_OBJECT_0;
                        break;

                    case WAIT_OBJECT_0 + 1: // Kill requested
                        running = false;
                        break;

                    default:
                        _ASSERT_EXPR(FALSE, L"Wait error");
                        break;
                }

                if (!running) break;

                // continue working...

                break;
            }

            case WAIT_OBJECT_0 + 1: // Kill requested
                running = false;
                break;

            default:
                _ASSERT_EXPR(FALSE, L"Wait error");
                break;
        }
    }
    while (running);

    return 0;
}

答案 1 :(得分:0)

这里我不使用事件,而是使用'command'(运行,暂停,退出)将Apc队列到此线程。但需要了解更多关于任务的信息,以获得最佳解决方案你在写服务吗?

struct DAQ 
{
    HANDLE _hEvent;

    enum STATE {
        running,
        paused,
        exit
    } _state;

    DAQ()
    {
        _hEvent = 0;
    }

    ~DAQ()
    {
        if (_hEvent)
        {
            ZwClose(_hEvent);
        }
    }

    NTSTATUS Init()
    {
        return ZwCreateEvent(&_hEvent, EVENT_ALL_ACCESS, 0, NotificationEvent, FALSE);
    }

    void Close()
    {
        if (HANDLE hEvent = InterlockedExchangePointer(&_hEvent, 0))
        {
            ZwClose(hEvent);
        }
    }

    DWORD fakeOutFn()
    {
        DbgPrint("running\n");
        _state = running;
        ZwSetEvent(_hEvent, 0);
        static LARGE_INTEGER Interval = { 0, MINLONG };
        do ; while (0 <= ZwDelayExecution(TRUE, &Interval) && _state != exit);

        DbgPrint("exit\n");
        return 0;
    }

    static DWORD WINAPI _fakeOutFn(PVOID pThis)
    {
        return ((DAQ*)pThis)->fakeOutFn();
    }

    void OnApc(STATE state)
    {
        _state = state;

        static PCSTR stateName[] = { "running", "paused" };

        if (state < RTL_NUMBER_OF(stateName))
        {
            DbgPrint("%s\n", stateName[state]);
        }
    }

    static void WINAPI _OnApc(PVOID pThis, PVOID state, PVOID)
    {
        ((DAQ*)pThis)->OnApc((STATE)(ULONG_PTR)state);
    }
};

void test()
{
    DAQ d;
    if (0 <= d.Init())
    {
        if (HANDLE hThread = CreateThread(0, 0, DAQ::_fakeOutFn, &d, 0, 0))
        {
            if (STATUS_SUCCESS == ZwWaitForSingleObject(d._hEvent, FALSE, 0))// need for not QueueApc too early. in case ServiceMain this event not need
            {
                d.Close();
                int n = 5;
                do 
                {
                    DAQ::STATE state;
                    if (--n)
                    {
                        state = (n & 1) != 0 ? DAQ::running : DAQ::paused;
                    }
                    else
                    {
                        state = DAQ::exit;
                    }
                    ZwQueueApcThread(hThread, DAQ::_OnApc, &d, (PVOID)state, 0);
                } while (n);
            }
            ZwWaitForSingleObject(hThread, FALSE, 0);
            ZwClose(hThread);
        }
    }
}