2013-03-03 12 views
41

Aşağıdaki test kodu vardır: Ben herhangi bir istisna atmayın iplik yöntemlerindeTaskCanceledException neden oluşuyor?

void Button_Click(object sender, RoutedEventArgs e) 
{ 
    var source = new CancellationTokenSource(); 

    var tsk1 = new Task(() => Thread1(source.Token), source.Token); 
    var tsk2 = new Task(() => Thread2(source.Token), source.Token); 

    tsk1.Start(); 
    tsk2.Start(); 

    source.Cancel(); 

    try 
    { 
     Task.WaitAll(new[] {tsk1, tsk2}); 
    } 
    catch (Exception ex) 
    { 
     // here exception is caught 
    } 
} 

void Thread1(CancellationToken token) 
{ 
    Thread.Sleep(2000); 

    // If the following line is enabled, the result is the same. 
    // token.ThrowIfCancellationRequested(); 
} 

void Thread2(CancellationToken token) 
{ 
    Thread.Sleep(3000); 
} 

ama görevleri başlatır dış kod try-catch bloğunda TaskCanceledException olsun. Bu neden olur ve bu durumda token.ThrowIfCancellationRequested();'un amacı nedir? Konu yönteminde yalnızca token.ThrowIfCancellationRequested(); numaralı telefonu çağırırsam istisnanın atılması gerektiğine inanıyorum.

+0

Bu, VS2017 .NET Framework 4.6.2'de bir istisna oluşturmaz. –

cevap

19

Bunun beklenen bir davranış olduğuna inanıyorum çünkü bir yarış durumu varyasyonuna giriyorsunuz. How to: Cancel a task and its children itibaren

:

zorla görevi bitmiyor çağıran iş parçacığı; sadece iptal talebinde bulunduğunu gösterir. Görev zaten çalışıyorsa, isteği fark etmek ve uygun şekilde yanıt vermek kullanıcı delegesine kalmıştır. Görev başlamadan önce iptal istenirse, kullanıcı temsilcisi hiçbir zaman çalıştırılmaz ve görev nesnesi Canceled durumuna geçer.

ve Task Cancellation den

:

Sen [...], yetki verilen dönerek işlemi kesebilirler. Birçok senaryoda bu yeterlidir; ancak, bu şekilde "iptal edilen" bir görev örneği,durumuna değil, RanToCompletion durumuna geçiş yapar. Burada

Benim eğitimli tahminim size iki görevler üzerinde .Start() aradığınız iken, şansını CancellationTokenSource üzerinde .Cancel() aramadan önce bir (veya her ikisi) aslında başlamadı olmasıdır. Bahse girerim, görevlerin başlangıcı ile iptal arasında en az üç saniye beklerseniz, istisna alamaz. Ayrıca, her iki görevin de .Status özelliğini kontrol edebilirsiniz. Haklıysam, .Status özelliğinin, istisna atıldığında bunlardan en az birinde TaskStatus.Canceled okuması gerekir.

Yeni bir Task başlatmanın yeni bir iş parçacığı oluşturulduğunu garanti etmediğini unutmayın. Yeni bir iş parçacığının neye dönüştüğünü ve yalnızca yürütme için neyin sıraya konulduğuna karar vermek TPL'ye düşer.

+0

Evet, bu doğru. "Yeni bir iş parçacığı neyin alınacağına karar vermek için TPL'ye düşüyor" dışında, aslında soruda Start() yöntemi hemen ThreadPool üzerindeki görevi sıraya alır ve iş parçacığı havuzu (TPL'den daha düşük olan yığın) ne zaman karar verir Aslında işi yürütmek için. Ancak, eğer iş gerçekten yürütmeden önce jeton iptal edilirse, görev yine de iptal olur. –

+3

'Task.WaitAll ', bir iş parçacığını, asenkron çalışma olabilecekleri için beklerken engellediği için biraz kötüdür. Bunun yerine "Task.WhenAll" i çağırırsanız, sadece bir iş parçacığı engelini kaldırmakla kalmaz, aynı zamanda iptal edilen görevlere de basmaz. Ancak, o zaman Wait() veya 'bekliyor' ise, yöntemin atayacağı görev. –

+1

Yani, bu tasarım gereği, bir görev iptal edilmeden önce her zaman istisna atar. –

İlgili konular