2017-12-20 7 views
5

저는 코드 조각에 교착 상태가 발생했습니다. 고맙게도, 나는 아래 예제에서 문제를 재현 할 수있었습니다. 정상적인 .Net Core 2.0 콘솔 응용 프로그램으로 실행하십시오. 다음과 같이 내가 기대했던 어떤교착 상태의 원인은 무엇입니까?

class Class2 
{ 

    static void Main(string[] args) 
    { 
     Task.Run(MainAsync); 
     Console.WriteLine("Press any key..."); 
     Console.ReadKey(); 
    } 

    static async Task MainAsync() 
    { 
     await StartAsync(); 
     //await Task.Delay(1); //a little delay makes it working 
     Stop(); 
    } 


    static async Task StartAsync() 
    { 
     var tcs = new TaskCompletionSource<object>(); 
     StartCore(tcs); 
     await tcs.Task; 
    } 


    static void StartCore(TaskCompletionSource<object> tcs) 
    { 
     _cts = new CancellationTokenSource(); 
     _thread = new Thread(Worker); 
     _thread.Start(tcs); 
    } 


    static Thread _thread; 
    static CancellationTokenSource _cts; 


    static void Worker(object state) 
    { 
     Console.WriteLine("entering worker"); 
     Thread.Sleep(100); //some work 

     var tcs = (TaskCompletionSource<object>)state; 
     tcs.SetResult(null); 

     Console.WriteLine("entering loop"); 
     while (_cts.IsCancellationRequested == false) 
     { 
      Thread.Sleep(100); //some work 
     } 
     Console.WriteLine("exiting worker"); 
    } 


    static void Stop() 
    { 
     Console.WriteLine("entering stop"); 
     _cts.Cancel(); 
     _thread.Join(); 
     Console.WriteLine("exiting stop"); 
    } 

} 

는 전체 순서입니다 : 나는를 삽입 할 경우,

Press any key... 
entering worker 
entering stop 

마지막 :

Press any key... 
entering worker 
entering loop 
entering stop 
exiting worker 
exiting stop 

그러나, 실제 시퀀스가 ​​Thread.Join 통화 포장 마차

MainAsync 본문에 작은 지연, 모든게 잘 간다. 교착 상태가 발생하는 이유는 무엇입니까?

참고 : 원래 코드에서 TaskCompletionSource 대신 SemaphoreSlim을 사용하여 해결했으며 전혀 문제가 없습니다. 문제가있는 곳만 이해하고 싶습니다.

+1

'작업'과'스레드'를 섞어서 사용하는 이유는 무엇입니까? – Groo

+0

'var tcs = new TaskCompletionSource (TaskCreationOptions.RunContinuationsAsynchronously);'로 변경하면 예상대로 실행됩니다. 이제'SetResult'는 continuations을 동 기적으로 실행하고'Continue '는'SetResult'가 실행 된 쓰레드를 조인하여'dead()'를 호출합니다. – Evk

+0

@Groo 원래 코드에는 작업을 혼합하지 않고 여러 작업을 수행하는 장기 실행 스레드 (작업자)가 있습니다. 그러나 초기 작업이 완료되었을 때 완료되는 작업자/스레드를 시작하기 위해 노출되는 기다릴 수있는 기능이 있습니다. –

답변

3

tcs.SetResult(null);Worker()에 대한 호출은 기본 작업이 완료 될 때까지 반환되지 않습니다 (자세한 내용은 this question을 확인하십시오). 당신이 작업 상태를 경우하는 것은 당신이 교착 상태를 얻을 이유가 WaitingForActivation입니다 :

  1. tcs.SetResult(null) 호출에 의해 차단 Worker()을 실행 스레드.

  2. 스레드 실행 Stop()_thread.Join() 호출에 의해 차단됩니다.

+0

도움을 주셔서 감사합니다! –

0

MainAsync() 스레드가 다른 스레드보다 '빠릅니다'. 그리고 스레드가 아닌 작업에서만 제어를 수행하고 있습니다.!

MainAsync() 메서드에서 StartAsync() 메서드를 기다리고 작업을 완료 한 다음 스레드를 시작합니다. 방법 StartAsync()이 작업 (생성되고 시작된 스레드)과 함께 완료되면 해당 기능은 작업 완료에 대해 MainAsync()을 알립니다. 그런 다음 MainAsync()은 Stop 메소드를 호출합니다. 하지만 스레드는 어디에 있습니까? 그것은 아무런 제어없이 병렬로 실행되고 작업을 끝내려고합니다. 이것은 교착 상태가 아니며 작업과 스레드간에 동기화가 없습니다.

await Task.Delay(1) 코드를 넣을 때 작업이 끝나기 전에 스레드가 충분히 빠르기 때문에 코드가 작동하는 이유는 무엇입니까 (thread.join).

+0

hmm .... 우선, StartAsync는 SetResult 메서드에 의해 TaskCompletionSource가 해제 될 때 종료되고 스레드 내부에 있습니다. 두 번째로, 토큰이 취소 된 것으로 표시되지 않으면 스레드가 종료되지 않아야합니다 (Stop 메서드에 있음). –

관련 문제