2017-09-13 3 views
1

이것은 WPF.NET에서 직면 한 질문입니다.WPF에서 ManualResetEvent 및 비동기 메서드를 사용할 때 교착 상태가 발생하는 이유는 무엇입니까?

public class TaskRunnerWithProgressFeedback(){ 
    ManualResetEvent _event = new ManualResetEvent(false); 
    public void RunTask(Action action) { 
     _event.Reset(); 
     //Display loading screen 
     RunAsync(action); 
     Console.WriteLine("Load completed."); 
     //Hide loading screen 
     _event.WaitOne(); 
    } 

    private async void RunAsync(Action action) { 
     await Task.Run(() => action.Invoke()); 
     _event.Set(); 
    } 
} 

그래서 내가 여기에이 클래스를 가지고 있고, 나는 UI 스레드에서 RunTask 메소드를 호출 : 문제를 설명하기 위해, 우리는 아래의 클래스를 살펴 봅시다. 예를 들어 :

private void Button1_OnClick(object sender , RoutedEventArgs e) { 
    var x = new TaskRunnerWithProgressFeedback(); 
    x.RunTask(()=>{ /*Some time-consuming action*/ }); 
} 

그리고 Button1을 클릭하면, 전체 프로그램이 교착 상황으로 실행됩니다. 이 상황에 대한 설명이 있습니까?

발 참고 사항 : 행동 테스트를 수행하려면 TaskRunnerWithProgressFeedback 클래스가 필요합니다. 나는 그 테스트를 깨기 때문에 BackgroundWorker을 사용하지 않을 것이다.

+1

WaitOne()은 어디에 있나요? – Sinatr

+1

'await'없이'async' 메소드를 호출하지 마십시오. 그리고 이벤트 핸들러를 제외한 다른 곳에서는'async void' 메소드를 사용하지 마십시오. – dymanoid

+0

죄송합니다. 사고였습니다. 다시 추가했습니다. –

답변

2

RunAsync 메서드에서 만든 Task 메서드가 완료되면 _event.Set()에 대한 호출이 UI 스레드에서 실행되는 것으로 가정합니다.

해당 작업이 완료되기 전에 UI 스레드에서 _event.WaitOne()을 호출하면 교착 상태가 발생합니다.

ManualResetEvent이 설정 될 수 있도록하지만이 WaitOne() 호출에 의해 차단되어 있기 때문에 결코 Set() 메서드를 호출하는 코드가 UI 스레드에서 실행되지 수 없습니다되기 때문에 다음 UI 스레드가 대기 때문입니다.

이것은 기본적으로 async/await의 작동 방식입니다. async 메서드는 히트 될 때까지 동 기적으로 실행되고 await, 호출자에게 반환됩니다. 컨텍스트 (이 경우 디스패처 스레드)가 캡처되고 async 메서드의 나머지 부분은 대기 메서드가 완료되면 async 메서드가 호출 된 동일한 디스패처/UI 스레드에서 다시 실행됩니다.

그러나 나머지 방법은 물론 컨텍스트 스레드가 해제 될 때까지 실행할 수 없습니다. 그리고이 경우에는 async 메소드의 나머지 부분이 Set() => 교착 상태가 될 때까지 기다리기 때문에 결코 존재하지 않습니다.

+0

이것은 문제의 훌륭한 설명입니다. 이 문제를 해결하려면 [My AsyncEx library] (https://github.com/StephenCleary/AsyncEx)에서 'AsyncManualResetEvent'를 사용할 수 있습니다. –