2013-03-03 12 views
41

मैं निम्नलिखित परीक्षण कोड है:TaskCanceledException क्यों होता है?

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

धागा तरीकों मैं किसी भी अपवाद फेंक नहीं है, लेकिन मैं बाहरी कोड जो कार्य शुरू होता है की try-catch ब्लॉक में TaskCanceledException मिलता है। ऐसा क्यों होता है और इस मामले में token.ThrowIfCancellationRequested(); का उद्देश्य क्या है। मेरा मानना ​​है कि अपवाद केवल तभी फेंक दिया जाना चाहिए यदि मैं थ्रेड विधि में token.ThrowIfCancellationRequested(); पर कॉल करता हूं।

+0

यह वीएस2017 .NET Framework 4.6.2 में अपवाद नहीं फेंकता है। –

उत्तर

19

मुझे विश्वास है कि यह व्यवहार की उम्मीद है क्योंकि आप दौड़ की स्थिति में बदलाव कर रहे हैं।

How to: Cancel a task and its children से:

बुला धागा जबरन काम खत्म नहीं होता; यह केवल सिग्नल करता है कि रद्दीकरण का अनुरोध किया जाता है। यदि कार्य पहले से चल रहा है, तो यह अनुरोध पर ध्यान देने और उचित प्रतिक्रिया देने के लिए उपयोगकर्ता प्रतिनिधि पर निर्भर करता है। यदि कार्य चलाने से पहले रद्दीकरण का अनुरोध किया जाता है, तो उपयोगकर्ता प्रतिनिधि कभी निष्पादित नहीं होता है और कार्य ऑब्जेक्ट Canceled स्थिति में संक्रमण करता है।

और Task Cancellation से:

आप द्वारा [...], प्रतिनिधि से लौटने के आपरेशन समाप्त कर सकते हैं। कई परिदृश्यों में यह पर्याप्त है; हालांकि, एक कार्य उदाहरण जो RanToCompletion राज्य में संक्रमणों को इस तरह से "रद्द" किया गया है, Canceled राज्य नहीं।

मेरे अनुमान लगाने यहाँ कि जब आप अपने दो कार्यों पर .Start() बुला रहे हैं, संभावना है कि एक (या उन दोनों) वास्तव में इससे पहले कि आप अपने CancellationTokenSource पर .Cancel() बुलाया शुरू नहीं किया जाता है। मैं शर्त लगाता हूं कि यदि आप कार्यों और रद्दीकरण की शुरुआत के बीच कम से कम तीन सेकंड प्रतीक्षा करते हैं, तो यह अपवाद नहीं फेंक देगा। साथ ही, आप दोनों कार्यों की .Status संपत्ति की जांच कर सकते हैं। यदि मैं सही हूं, तो .Status संपत्ति को कम से कम एक पर TaskStatus.Canceled पढ़ना चाहिए जब अपवाद फेंक दिया जाता है।

याद रखें, एक नया Task शुरू करने से कोई नया थ्रेड बनने की गारंटी नहीं है। यह तय करने के लिए टीपीएल में पड़ता है कि नया धागा क्या होता है और निष्पादन के लिए बस कतार में क्या होता है।

+0

हां, यह सही है। इसके अलावा "यह तय करने के लिए टीपीएल में पड़ता है कि नया धागा क्या प्राप्त होता है", वास्तव में प्रश्न में प्रारंभ() विधि तुरंत थ्रेडपूल पर कार्य को कतार में रखती है, और धागा पूल (जो टीपीएल की तुलना में ढेर में कम होता है) तय करता है वास्तव में काम निष्पादित करने के लिए। लेकिन, अगर काम को वास्तव में निष्पादित करने से पहले टोकन रद्द कर दिया गया है, तो कार्य अभी भी रद्द हो जाएगा। –

+3

'टास्क। वैटएल 'कुछ हद तक खराब है, क्योंकि यह अतुल्यकालिक काम के इंतजार के दौरान थ्रेड को अवरुद्ध करता है। यदि आप इसके बजाए 'कार्य। तब सभी' कॉल करते हैं, न केवल आप थ्रेड को अनब्लॉक करेंगे, लेकिन यह रद्द किए गए कार्यों पर भी फेंक नहीं देगा। यदि आप उस समय प्रतीक्षा करें() या 'प्रतीक्षा करें' तो कार्य उस विधि को फेंक देगा। –

+1

तो यह डिज़ाइन द्वारा है, यदि कोई कार्य शुरू होने से पहले रद्द कर दिया गया तो यह हमेशा अपवाद फेंक देगा? –

संबंधित मुद्दे