다음을 통해 공유


DispatcherQueue

중요 사항

  • Windows 앱 SDK DispatcherQueue 클래스는 스레드에 대한 작업이 직렬 방식으로 실행되는 우선 순위가 지정된 큐를 관리합니다.
  • 백그라운드 스레드가 DispatcherQueue의 스레드에서 코드를 실행하는 방법을 제공합니다(예: 스레드 선호도가 있는 개체가 라이브인 UI 스레드).
  • 클래스는 임의 메시지 루프와 정확하게 통합됩니다. 예를 들어 중첩된 메시지 루프의 일반적인 Win32 관용구를 지원합니다.
  • AppWindow 클래스는 DispatcherQueue와 통합됩니다. 지정된 스레드에 대한 DispatcherQueue가 종료되면 AppWindow 인스턴스가 자동으로 제거됩니다.
  • 시간 제한이 만료되면 호출되는 대리자를 등록하는 방법을 제공합니다.
  • 메시지 루프가 종료될 때 구성 요소에 알리고, 선택적으로 미해결 작업이 완료될 때까지 종료를 연기하는 이벤트를 제공합니다. 이렇게 하면 DispatcherQueue를 사용하지만 메시지 루프를 소유하지 않는 구성 요소가 루프가 종료될 때 스레드에서 정리를 수행할 수 있습니다.
  • DispatcherQueue는 스레드 싱글톤입니다(지정된 스레드에서 실행되는 스레드는 대부분 있을 수 있습니다). 기본적으로 스레드에는 DispatcherQueue가 없습니다.
  • 스레드 소유자는 DispatcherQueueController를 만들어 스레드에 대한 DispatcherQueue를 초기화할 수 있습니다. 이 시점에서 모든 코드는 스레드의 DispatcherQueue에 액세스할 수 있지만 DispatcherQueueController의 소유자만 DispatcherQueue를 드레이닝하고 ShutdownStartedShutdownCompleted 이벤트를 발생시키는 DispatcherQueueController.ShutdownQueue 메서드에 액세스할 수 있습니다.
  • 최외각 메시지 루프 소유자는 DispatcherQueue 인스턴스를 만들어야 합니다. 스레드의 최외각 메시지 루프 실행을 담당하는 코드만 디스패치가 완료된 시기를 알 수 있으며, 이는 DispatcherQueue를 종료하기에 적절한 시간입니다. 즉, DispatcherQueue를 사용하는 구성 요소는 스레드의 메시지 루프를 소유하지 않는 한 DispatcherQueue를 만들지 않아야 합니다.

런다운

스레드가 이벤트 루프를 종료한 후 DispatcherQueue를 종료해야 합니다. 이렇게 하면 ShutdownStartingShutdownCompleted 이벤트가 발생하며, 큐에 추가 큐를 사용하지 않도록 설정하기 전에 큐에 대기 중인 최종 항목을 드레이닝합니다.

  • DispatcherQueue 소유 메시지 루프를 사용하여 전용 스레드에서 실행되는 DispatcherQueue를 종료하려면 DispatcherQueueController.ShutdownQueueAsync 메서드를 호출합니다.
  • 앱이 임의의 메시지 루프(예: XAML Islands)를 소유하는 시나리오의 경우 동기 DispatcherQueueController.ShutdownQueue 메서드를 호출합니다. 이 메서드는 종료 이벤트를 발생시키고 호출 스레드에서 DispatcherQueue 를 동기적으로 드레이닝합니다.

DispatcherQueueController.ShutdownQueueAsync 또는 DispatcherQueueController.ShutdownQueue를 호출하는 경우 발생하는 이벤트의 순서는 다음과 같습니다.

  • ShutdownStarting. 처리할 앱용입니다.
  • FrameworkShutdownStarting. 처리할 프레임워크용입니다.
  • FrameworkShutdownCompleted. 처리할 프레임워크용입니다.
  • ShutdownCompleted. 처리할 앱용입니다.

순서대로 종료할 수 있도록 이벤트는 애플리케이션/프레임워크 범주로 구분됩니다. 즉, 프레임워크 종료 이벤트에 앞서 애플리케이션 종료를 명시적으로 발생시켜 애플리케이션이 종료될 때 프레임워크 구성 요소가 사용할 수 없는 상태가 될 위험이 없습니다.

namespace winrt 
{
    using namespace Microsoft::UI::Dispatching;
}

// App runs its own custom message loop.
void RunCustomMessageLoop()
{
    // Create a DispatcherQueue.
    auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()};

    // Run a custom message loop. Runs until the message loop owner decides to stop.
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!ContentPreTranslateMessage(&msg))
        {
            TranslateMesasge(&msg);
            DispatchMessage(&msg);
        }
    }

    // Run down the DispatcherQueue. This single call also runs down the system DispatcherQueue
    // if one was created via EnsureSystemDispatcherQueue:
    // 1. Raises DispatcherQueue.ShutdownStarting event.
    // 2. Drains remaining items in the DispatcherQueue, waits for deferrals.
    // 3. Raises DispatcherQueue.FrameworkShutdownStarting event.
    // 4. Drains remaining items in the DispatcherQueue, waits for deferrals.
    // 5. Disables further enqueuing.
    // 6. Raises the DispatcherQueue.FrameworkShutdownCompleted event.
    // 7. Raises the DispatcherQueue.ShutdownCompleted event.    

    dispatcherQueueController.ShutdownQueue();
}

최외각 및 재귀 메시지 루프

DispatcherQueue 는 사용자 지정 메시지 루프를 지원합니다. 그러나 사용자 지정이 필요하지 않은 간단한 앱의 경우, 기본 구현을 제공합니다. 이는 개발자의 부담을 없애고 일관되게 올바른 동작을 보장하는 데 도움이 됩니다.

namespace winrt 
{
    using namespace Microsoft::UI::Dispatching;
}

// Simple app; doesn't need a custom message loop.
void RunMessageLoop()
{
    // Create a DispatcherQueue.
    auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()};

    // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage.
    dispatcherQueueController.DispatcherQueue().RunEventLoop();

    // Run down the DispatcherQueue. 
    dispatcherQueueController.ShutdownQueue();
}

// May be called while receiving a message.
void RunNestedLoop(winrt::DispatcherQueue dispatcherQueue)
{
    // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage.
    dispatcherQueue.RunEventLoop();
}

// Called to break out of the message loop, returning from the RunEventLoop call lower down the
// stack.
void EndMessageLoop(winrt::DispatcherQueue dispatcherQueue)
{
    // Alternatively, calling Win32's PostQuitMessage has the same effect.
    dispatcherQueue.EnqueueEventLoopExit();
}

시스템 디스패처 관리

일부 Windows 앱 SDK 구성 요소(예: MicaController)는 스레드에서 실행되는 시스템 DispatcherQueue(Windows.System.DispatcherQueue)가 필요한 시스템 구성 요소에 따라 달라집니다.

이러한 경우 시스템 DispatcherQueue 종속성이 있는 구성 요소는 EnsureSystemDispatcherQueue 메서드를 호출하여 앱이 시스템 DispatcherQueue를 관리할 수 없도록 합니다.

이 메서드를 호출하면 Windows 앱 SDK DispatcherQueue는 시스템 DispatcherQueue의 수명을 자동으로 관리하여 Windows 앱 SDK DispatcherQueue와 함께 시스템 DispatcherQueue를 종료합니다. 구성 요소는 메시지 루프가 종료된 후 적절히 정리하기 위해 Windows 앱 SDK 및 시스템 DispatcherQueue 종료 이벤트를 둘 다 사용합니다.

namespace winrt 
{
    using namespace Microsoft::UI::Composition::SystemBackdrops;
    using namespace Microsoft::UI::Dispatching;
}

// The Windows App SDK component calls this during its startup.
void MicaControllerInitialize(winrt::DispatcherQueue dispatcherQueue)
{
    dispatcherQueue.EnsureSystemDispatcherQueue();

    // If the component needs the system DispatcherQueue explicitly, it can now grab it off the thread.
    winrt::Windows::System::DispatcherQueue systemDispatcherQueue =
        winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
}

void AppInitialize()
{
    // App doesn't need to concern itself with the system DispatcherQueue dependency.
    auto micaController = winrt::MicaController();
}

AppWindow 통합

AppWindow 클래스에는 DispatcherQueue와 통합하는 기능이 있으므로 DispatcherQueueController.ShutdownQueueAsync 또는 DispatcherQueueController.ShutdownQueue 메서드가 호출될 때 AppWindow 개체가 자동으로 제거될 수 있습니다.

호출자가 AppWindow와 연결된 DispatcherQueue를 검색하여 컴퍼지션입력 네임스페이스의 다른 개체와 정렬할 수 있는 AppWindow 속성도 있습니다.

AppWindowDispatcherQueue를 인식하기 위한 명시적 설정이 필요합니다.

namespace winrt 
{
    using namespace Microsoft::UI::Dispatching;
    using namespace Microsoft::UI::Windowing;
}

void Main()
{
    // Create a Windows App SDK DispatcherQueue.
    auto dispatcherQueueController{winrt::DispatcherQueueController::CreateOnCurrentThread()};

    var appWindow = AppWindow.Create(nullptr, 0, dispatcherQueueController.DispatcherQueue());

    // Since we associated the DispatcherQueue above with the AppWindow, we're able to retreive it 
    // as a property. If we were to not associate a dispatcher, this property would be null.
    ASSERT(appWindow.DispatcherQueue() == dispatcherQueueController.DispatcherQueue());

    // Runs a message loop until a call to DispatcherQueue.EnqueueEventLoopExit or PostQuitMessage.
    dispatcherQueueController.DispatcherQueue().RunEventLoop();

    // Rundown the Windows App SDK DispatcherQueue. While this call is in progress, the AppWindow.Destoyed
    // event will be raised since the AppWindow instance is associated with the DispatcherQueue.
    dispatcherQueueController.ShutdownQueue();
}