XTaskQueueTerminate

通过取消所有挂起的项并阻止新项排队来终止任务队列。

语法

HRESULT XTaskQueueTerminate(  
         XTaskQueueHandle queue,  
         bool wait,  
         void* callbackContext,  
         XTaskQueueTerminatedCallback* callback  
)  

参数

queue _In_
类型:XTaskQueueHandle

要终止的队列。

wait _In_
类型:bool

为 true 则等待终止完成。

callbackContext _In_opt_
类型:void*

传递给回调的一个可选上下文指针。

callback _In_opt_
类型:XTaskQueueTerminatedCallback*

队列终止后将调用的可选回调。

返回值

类型:HRESULT

HRESULT 成功或错误代码。

备注

注意

在时间敏感线程上调用此函数是不安全的。 有关详细信息,请参阅时间敏感线程

XTaskQueueCloseHandle 仅使任务队列对象的内部引用计数递减。 如果回调仍在队列中,这些回调将持有对队列对象的引用,仍可能调用它们。 这可能引发应用关闭问题。 应用关闭时,它需要确保在清理后没有虚假回调执行。 XTaskQueue 提供 XTaskQueueTerminate API 来执行队列的受控终止。

终止任务队列执行以下操作:

  1. 调用两个端口的所有回调,并将 canceled 参数设置为 true。
  2. 将调度在工作端口上挂起的所有回调。 向工作端口提交新回调将失败,并返回 E_ABORT。
  3. 将调度在完成端口上挂起的所有回调。 向完成端口提交新回调将失败,并返回 E_ABORT。

此进程完成后,XTaskQueueTerminate 将返回 wait 是否为 true。 如果 wait 为 false,将异步终止。 如果您提供终止回调,将在终止结束时从完成线程调用它。

注意

  • XTaskQueueTerminate 不关闭队列句柄。 终止后,您仍需要调用 XTaskQueueCloseHandle
  • 如果您在某线程上调用 XTaskQueueTerminate,而该线程正在通过调用 XTaskQueueDispatch 处理队列回调,请不要为 wait 参数传递 true,否则您的代码可能死锁。

以下示例演示如何终止以前创建的一个任务队列。

注意

SubmitCallback 是在 XTaskQueueSubmitCallback 函数的代码示例中定义的帮助程序函数。

void CreatingTaskQueue()
{
    XTaskQueueHandle queue;
    HRESULT hr = XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::ThreadPool, &queue);
    if (FAILED(hr))
    {
        printf("Creating queue failed: 0x%x\r\n", hr);
        return;
    }

    SubmitCallbacks(queue);

    // Wait a while for the callbacks to run
    Sleep(1000);

    XTaskQueueTerminate(queue, true, nullptr, nullptr);
}

任务队列可以与 UI 线程集成。 通常,您希望在 UI 线程上运行在完成端口排队的回调。 此示例使用线程池来执行工作,但将完成端口回调集成到 Win32 窗口进程中。 它还演示了在将某一任务队列与其他线程模型集成时如何正确终止任务队列。

struct WorkData
{
    HWND hwnd;
    WCHAR text[80];
};

void CALLBACK WorkCompletion(void* context, bool cancel)
{
    WorkData* data = (WorkData*)context;

    if (!cancel)
    {
        SetWindowText(data->hwnd, data->text);
    }

    delete data;
}

void CALLBACK BackgroundWork(void* context, bool cancel)
{
    if (!cancel)
    {
        WorkData* data = new WorkData;
        data->hwnd = (HWND)context;

        if (GetTimeFormatEx(
            LOCALE_NAME_USER_DEFAULT, 0, nullptr, 
            nullptr, data->text, 80) == 0)
        {
            swprintf_s(data->text, L"Error : %d", GetLastError());
        }

        // Now take our formatted string and submit it as a completion callback
        XTaskQueueSubmitCallback(
            g_queue,
            XTaskQueuePort::Completion, 
            data, 
            WorkCompletion);
    }
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    HRESULT hr;

    switch (msg)
    {
    case WM_CREATE:
        
        // We will do work on the thread pool, but completion
        // callbacks should be manual so we can integrate them with
        // the message loop.

        hr = XTaskQueueCreate(
            XTaskQueueDispatchMode::ThreadPool,
            XTaskQueueDispatchMode::Manual,
            &g_queue);

        if (SUCCEEDED(hr))
        {
            hr = XTaskQueueRegisterMonitor(g_queue, hwnd, 
                [](void* context, XTaskQueueHandle, XTaskQueuePort port)
            {
                // If a new callback was submitted to the completion port, post a message
                // so we dispatch it in our message loop
                if (port == XTaskQueuePort::Completion)
                {
                    HWND hwnd = static_cast<HWND>(context);
                    PostMessage(hwnd, WM_QUEUE_COMPLETION, 0, 0);
                }
            }, &g_monitorToken);
        }

        if (FAILED(hr))
        {
            PostQuitMessage(1);
            return 0;
        }
        break;
      
    case WM_LBUTTONDOWN:
        hr = XTaskQueueSubmitCallback(
            g_queue,
            XTaskQueuePort::Completion,
            hwnd,
            BackgroundWork);

        if (FAILED(hr))
        {
            MessageBox(hwnd, L"Failed to submit callback.", L"Error", MB_OK);
        }
        break;

    case WM_QUEUE_COMPLETION:
        XTaskQueueDispatch(g_queue, XTaskQueuePort::Completion, 0);
        break;

    case WM_CLOSE:

        // Terminate the task queue.  When done, destroy our window.  The termination callback
        // is queued to the completion port, so it will already be on the UI thread.

        hr = XTaskQueueTerminate(g_queue, false, hwnd, [](void* context)
        {
            HWND hwnd = static_cast<HWND>(context);
            DestroyWindow(hwnd);
            XTaskQueueUnregisterMonitor(g_queue, g_monitorToken);
            XTaskQueueCloseHandle(g_queue);
        });

        if (SUCCEEDED(hr))
        {
            // Prevent DefWndProc from destroying our window because
            // the termination callback will do it.
            return 0;
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

void TestWndProc()
{
    WNDCLASS wndClass;
    ZeroMemory(&wndClass, sizeof(wndClass));
    wndClass.lpfnWndProc = WndProc;
    wndClass.lpszClassName = L"TestClass";
    wndClass.hInstance = GetModuleHandle(nullptr);
    wndClass.hbrBackground = GetSysColorBrush(COLOR_WINDOW);

    ATOM c = RegisterClass(&wndClass);

    HWND h = CreateWindow(L"TestClass", L"Window", 
        WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
        10, 10, 300, 100, nullptr, nullptr, 
        GetModuleHandle(nullptr), 0);

    if (!h)
    {
        return;
    }

    MSG m;

    while (GetMessage(&m, nullptr, 0, 0))
    {
        TranslateMessage(&m);
        DispatchMessage(&m);
    }
}

要求

头文件:XTaskQueue.h

库:xgameruntime.lib

支持平台:Windows、Xbox One 系列主机和 Xbox Series 主机

另请参阅

XTaskQueue 成员
异步编程模型
异步任务队列设计