> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# XTaskQueueTerminate

> XTaskQueueTerminate

# XTaskQueueTerminate

보류 중인 모든 항목을 취소하고 새 항목이 큐에 추가되지 않도록 하여 작업 큐를 종료합니다.

## 구문

```cpp theme={null}
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 성공 또는 오류 코드입니다.

## 설명

<Note>이 함수는 시간에 민감한 스레드에서 호출하기에 안전하지 않습니다. 자세한 내용은 [시간에 민감한 스레드](https://learn.microsoft.com/gaming/gdk/docs/gdk-dev/console-dev/overviews/threads/time-sensitive-threads)를 참조하세요.</Note>

[XTaskQueueCloseHandle](/reference/system/xtaskqueue/functions/xtaskqueueclosehandle)은 단순히 작업 큐 개체의 내부 참조 횟수를 감소시킵니다. 큐에 아직 콜백이 있는 경우 해당 콜백은 큐 개체에 대한 참조를 유지하고 여전히 호출될 수 있습니다. 이로 인해 앱 종료 시 문제가 발생할 수 있습니다. 앱이 종료될 때 정리 후 가짜 콜백이 실행되지 않도록 해야 합니다. XTaskQueue는 큐를 제어된 방식으로 종료하는 **XTaskQueueTerminate** API를 제공합니다.

작업 큐 종료는 다음 작업을 수행합니다.

1. 두 포트의 모든 콜백이 *canceled* 매개 변수를 true로 설정하여 호출됩니다.
2. 작업 포트에서 보류 중인 모든 콜백이 디스패치됩니다. 작업 포트에 새 콜백을 제출하면 E\_ABORT로 실패합니다.
3. 완료 포트에서 보류 중인 모든 콜백이 디스패치됩니다. 완료 포트에 새 콜백을 제출하면 E\_ABORT로 실패합니다.

이 프로세스가 완료된 후 wait가 true이면 **XTaskQueueTerminate**가 반환됩니다. wait가 false이면 종료가 비동기적으로 발생합니다. 종료 콜백을 제공하면 종료의 끝에서 완료 스레드에서 호출됩니다.

<Note />

* **XTaskQueueTerminate**는 큐 핸들을 닫지 않습니다. 종료 후에도 [XTaskQueueCloseHandle](/reference/system/xtaskqueue/functions/xtaskqueueclosehandle)을 호출해야 합니다.
* [XTaskQueueDispatch](/reference/system/xtaskqueue/functions/xtaskqueuedispatch)를 호출하여 큐 콜백을 처리하는 스레드에서 **XTaskQueueTerminate**를 호출하는 경우, wait 매개 변수에 true를 전달하지 마세요. 그렇지 않으면 코드에서 교착 상태가 발생할 수 있습니다.

다음 예제에서는 이전에 만든 작업 큐를 종료하는 방법을 보여 줍니다.

<Note>**SubmitCallback**은 [XTaskQueueSubmitCallback](/reference/system/xtaskqueue/functions/xtaskqueuesubmitcallback) 함수에 대한 코드 예제에서 정의된 도우미 함수입니다.</Note>

```cpp theme={null}
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 창 프로시저에 통합합니다. 또한 다른 스레딩 모델과 통합할 때 작업 큐를 올바르게 종료하는 방법도 보여 줍니다.

```cpp theme={null}
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 콘솔

## 개념 문서

* [작업 큐 정리](/build/core-features/common/async/async-libraries/async-library-xtaskqueue-example-cleanup-task-queue)
* [시간에 민감한 스레드](https://learn.microsoft.com/gaming/gdk/docs/gdk-dev/console-dev/overviews/threads/time-sensitive-threads)

## 함께 보기

[XTaskQueue 멤버](/reference/system/xtaskqueue/xtaskqueue_members)\
[비동기 프로그래밍 모델](/build/core-features/common/async/async-programming-model)\
[비동기 작업 큐 디자인](/build/core-features/common/async/async-task-queue-design)
