2017-12-20 163 views
5

Tôi đang gặp vấn đề bế tắc trong một đoạn mã của tôi. Rất may, tôi đã có thể tái tạo vấn đề trong ví dụ dưới đây. Chạy như một ứng dụng Console Net 2.0 thông thường.Điều gì gây ra bế tắc?

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"); 
    } 

} 

gì tôi mong đợi là chuỗi hoàn chỉnh như sau:

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

Tuy nhiên, trình tự thực tế quầy hàng trên Thread.Join gọi:

Press any key... 
entering worker 
entering stop 

Cuối cùng, nếu tôi chèn một sự chậm trễ nhỏ trong cơ thể MainAsync, mọi thứ đều ổn. Tại sao (nơi) bế tắc xảy ra?

LƯU Ý: trong mã ban đầu tôi đã giải quyết bằng cách sử dụng SemaphoreSlim thay vì TaskCompletionSource và hoàn toàn không có vấn đề gì. Tôi chỉ muốn hiểu vấn đề ở đâu.

+1

Bất kỳ lý do nào bạn trộn 'Task's với' Thread's? – Groo

+0

Thay đổi thành 'var tcs = new TaskCompletionSource (TaskCreationOptions.RunContinuationsAsynchronously);' và nó sẽ chạy như bạn mong đợi. Bây giờ 'SetResult' chạy liên tục đồng bộ, và tiếp tục bao gồm lời gọi đến' Stop() ', mà chính nó tham gia vào chuỗi trong đó' SetResult' được thực hiện, do đó chúng bế tắc. – Evk

+0

@Groo trong mã ban đầu có một chuỗi dài chạy (nhân viên) thực hiện một số công việc mà không cần trộn nhiệm vụ. Tuy nhiên, có một hàm awaitable tiếp xúc để bắt đầu công nhân/thread hoàn thành khi một số công việc ban đầu đã được thực hiện. –

Trả lời

3

tcs.SetResult(null); gọi trong Worker() sẽ không trở lại cho đến khi tác vụ cơ bản được hoàn thành (hãy kiểm tra this question để biết chi tiết). Trong bạn là trường hợp tình trạng công việc là WaitingForActivation đó là lý do bạn nhận được một bế tắc:

  1. Chủ đề thi Worker() bị chặn bởi tcs.SetResult(null) gọi.

  2. Thực hiện chủ đề Stop() bị chặn bởi cuộc gọi _thread.Join().

+0

Cảm ơn bạn đã trợ giúp! –

0

MainAsync() chủ đề là 'nhanh' với thread khác. Và bạn chỉ thực hiện kiểm soát đối với các tác vụ không phải trên chủ đề!

Trong phương thức MainAsync() bạn đang chờ phương thức StartAsync() để hoàn thành công việc và sau đó bạn bắt đầu chuỗi. Khi phương thức StartAsync() được thực hiện với công việc của nó (tạo và bắt đầu luồng), hàm đó thông báo MainAsync() về việc hoàn thành công việc của nó. Sau đó, MainAsync() cuộc gọi Dừng phương pháp. Nhưng chủ đề của bạn ở đâu? Nó chạy song song mà không có bất kỳ kiểm soát và cố gắng để hoàn thành công việc của mình. Đây không phải là bế tắc, không có sự đồng bộ giữa nhiệm vụ và chuỗi.

Thats lý do tại sao khi bạn đặt await Task.Delay(1) mã của bạn hoạt động vì chuỗi là đủ nhanh để hoàn thành công việc trước khi tác vụ kết thúc nó (thread.join).

+0

hmm .... trước hết, StartAsync kết thúc khi TaskCompletionSource sẽ được phát hành theo phương pháp SetResult và bên trong chuỗi. Thứ hai, chủ đề KHÔNG nên kết thúc trừ khi mã thông báo sẽ được đánh dấu là đã hủy (và đó là trong phương thức Dừng). –

Các vấn đề liên quan