2019년 8월 5일 월요일

WPF 쓰레딩 모델, Invoke, BeginInvoke

일반적으로 WPF 응용 프로그램은 렌더링 처리와 UI 관리를 위한 두개의 쓰레드로 시작한다. UI 쓰레드는 작업 항목을 Dispatcher라는 오브젝트 내부에 대기시킨다. Dispatcher는 우선 순위에 따라 작업 항목을 선택하고 각각을 실행한다. 모든 UI 쓰레드에는 하나 이상의 Dispatcher가 있어야 하며 각 Dispatcher는 정확히 하나의 쓰레드에 의해서만 동작해야 한다.

응답성이 높은 애플리케이션을 구축하는 방법은 가능한 작업 항목의 실행 시간을 적게 유지하여 Dispatcher의 처리량(throughput)을 극대화 하는 것이다. 따라서 일반적으로 큰 작업은 별도의 쓰레드에서 처리하게 된다. 그리고 큰 작업이 완료되면 결과를 UI 쓰레드에 보고하여 표시하는 식이다.

WPF에서 대부분의 클래스는 DispatcherObject에서 파생된다. 생성시 DispatcherObject는 현재 실행 중인 쓰레드에 연결된 Dispatcher에 대한 참조를 저장한다. 프로그램을 실행하는 동안 DispatcherObject는 VerifyAccess 메서드를 호출할 수 있다. VerifyAccess 메서드는 현재 쓰레드와 연관된 Dispatcher와 생성시에 저장된 Dispatcher 참조 간의 비교 검사를 수행한다. 일치하지 않으면 VerifyAccess는 예외를 throw한다. VerifyAccess는 DispatcherObject에 속한 모든 메서드의 시작 부분에서 호출되도록 고안되었다.

...
하나의 쓰레드만 UI를 변경할 수 있다고 하였다. 그렇다면 백그라운드 쓰레드와 사용자 간 상호작용 하는 방법은 무엇인가? WPF에서 백그라운드 쓰레드는 UI 쓰레드에 작업을 수행하도록 요청할 수 있게 구성되어 있다. 백그라운드 쓰레드는 작업 항목을 UI 쓰레드의 Dispatcher에 등록할 수 있다. Dispatcher 클래스는 백그라운드 쓰레드가 작업 항목을 등록할 수 있게 'Invoke'와 'BeginInvoke' 메서드를 제공한다. Invoke는 동기 호출 방식이고 BeginInvoke는 비동기 호출 방식이다. 두 메서드 모두 실행을 위해 대리자를 스케줄에 등록시킨다.

아래와 같은 코드에서, Dispatcher는 startStopButton 컨트롤이 idle일 때만 delegate를 실행하게 된다. 우선순위가 SystemIdle로 지정되었으니 아래의 코드는 CheckNextNumber 호출보다 UI의 응답이 중요하다는 의미를 갖게 된다. Dispatcher는 Dispatcher 큐에 등록된 작업 항목을 우선 순위에 따라 정렬하여 순차적으로 처리한다. 제공되는 우선 순위 레벨은 10가지 이다. 이러한 우선 순위는 DispatcherPriority 열거 형에서 유지되고 관리된다.


public delegate void NextPrimeDelegate();

public void CheckNextNumber()
{
...
    if (continueCalculating)
    {
        startStopButton.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, new NextPrimeDelegate(this.CheckNextNumber));
    }
}

자세한 내용은 아래 링크를.
https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/threading-model

댓글 없음:

댓글 쓰기