टास्क कंस्ट्रक्टर में रद्दीकरण टोकन: क्यों?


223

कुछ System.Threading.Tasks.Taskनिर्माणकर्ता CancellationTokenएक पैरामीटर के रूप में लेते हैं :

CancellationTokenSource source = new CancellationTokenSource();
Task t = new Task (/* method */, source.Token);

इस बारे में मुझे क्या परेशान करता है कि वास्तव में टोकन में पारित होने के लिए विधि निकाय के अंदर से कोई रास्ता नहीं है (जैसे, कुछ भी नहीं Task.CurrentTask.CancellationToken)। टोकन को किसी अन्य तंत्र के माध्यम से प्रदान किया जाना चाहिए, जैसे कि राज्य वस्तु या लंबोदर में कब्जा कर लिया गया।

तो निर्माणकर्ता की सेवा में रद्दीकरण टोकन प्रदान करने का क्या उद्देश्य है?

जवाबों:


254

कंस्ट्रक्टर CancellationTokenमें उत्तीर्ण होकर Taskइसे कार्य के साथ जोड़ देता है।

एमएसडीएन से स्टीफन टूब का जवाब उद्धृत :

इसके दो प्राथमिक लाभ हैं:

  1. यदि Taskप्रारंभ करने से पहले टोकन को रद्द करने का अनुरोध किया गया है , तो Taskनिष्पादित नहीं होगा। संक्रमण करने के बजाय Running, यह तुरंत संक्रमण करेगा Canceled। यह कार्य को चलाने की लागत से बचता है यदि इसे वैसे भी चलाते समय रद्द कर दिया जाएगा।
  2. यदि टास्क का बॉडी कैंसिलेशन टोकन को भी मॉनिटर कर रहा है और OperationCanceledExceptionउस टोकन को (जिसमें वह ThrowIfCancellationRequestedकरता है) एक थ्रो फेंकता है , तब जब वह टास्क देखता है OperationCanceledException, तो यह चेक करता है कि क्या OperationCanceledExceptionटोकन टास्क के टोकन से मेल खाता है या नहीं। यदि ऐसा होता है, तो उस अपवाद को सहकारिता रद्द करने और राज्य Taskको संक्रमण Canceled(राज्य के बजाय Faulted) के रूप में देखा जाता है।

2
TPL इतनी अच्छी तरह से सोचा है।
कर्नल पैनिक

1
मुझे लगता है लाभ 1 को एक रद्दीकरण टोकन पारित करने के लिए इसी तरह लागू होता है Parallel.ForयाParallel.ForEach
कर्नल आतंक

27

निर्माणकर्ता आंतरिक रूप से रद्द करने से निपटने के लिए टोकन का उपयोग करता है। यदि आपका कोड टोकन तक पहुंच है, तो आप इसे स्वयं को पास करने के लिए जिम्मेदार हैं। मैं अत्यधिक CodePlex में Microsoft .NET पुस्तक के साथ समानांतर प्रोग्रामिंग पढ़ने की सिफारिश करूंगा ।

पुस्तक से सीटीएस का उदाहरण उपयोग:

CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;

Task myTask = Task.Factory.StartNew(() =>
{
    for (...)
    {
        token.ThrowIfCancellationRequested();

        // Body of for loop.
    }
}, token);

// ... elsewhere ...
cts.Cancel();

3
और क्या होगा अगर आप टोकन को पैरामीटर के रूप में पारित नहीं करते हैं? लगता है कि व्यवहार समान होगा, कोई उद्देश्य नहीं।
सर्गटेक

2
@sergdev: आप इसे कार्य और अनुसूचक के साथ पंजीकृत करने के लिए टोकन पास करते हैं। इसे पारित नहीं करना और इसका उपयोग करना अपरिभाषित व्यवहार होगा।
user7116

3
@sergdev: परीक्षण के बाद: myTask.IsCanceled और myTask.Status समान नहीं हैं जब आप टोकन को पैरामीटर के रूप में पास नहीं करते हैं। रद्द करने के बजाय स्थिति विफल हो जाएगी। फिर भी अपवाद एक ही है: यह दोनों में एक OperationCanceledException है।
ओलिवियर डी रिवोइरे

2
अगर मैं फोन नहीं करता तो क्या होगा token.ThrowIfCancellationRequested();? मेरे परीक्षण में, व्यवहार समान है। कोई विचार?
मशिनेरियम

1
@ कोबाल्टब्लू: नोप when cts.Cancel() is called the Task is going to get canceled and end, no matter what you do। यदि कार्य प्रारंभ होने से पहले रद्द हो जाता है, तो इसे रद्द कर दिया जाता है । यदि टास्क का शरीर बस किसी भी टोकन की जांच नहीं करता है, तो यह पूरा होने के लिए चलेगा, जिसके परिणामस्वरूप RanToCompletion स्थिति होगी। यदि बॉडी फेंकता है OperationCancelledException, उदाहरण के लिए ThrowIfCancellationRequested, तो टास्क जांच करेगा कि क्या एक्सेप्शन का कैंसेलेशनटोकन टास्क से जुड़ा हुआ है। यदि यह है, तो कार्य रद्द कर दिया गया है । यदि नहीं, तो यह दोषपूर्ण है
वोल्फज़ून

7

रद्दीकरण एक साधारण मामला नहीं है जितना कि कई लोग सोच सकते हैं। Msdn पर इस ब्लॉग पोस्ट में कुछ सूक्ष्मताएँ बताई गई हैं:

उदाहरण के लिए:

समानांतर एक्सटेंशन और अन्य प्रणालियों में कुछ स्थितियों में, उन कारणों के लिए एक अवरुद्ध विधि को जगाना आवश्यक है जो किसी उपयोगकर्ता द्वारा स्पष्ट रद्दीकरण के कारण नहीं हैं। उदाहरण के लिए, यदि blockingCollection.Take()संग्रह खाली होने के कारण एक धागा बंद है और दूसरा धागा बाद में कॉल करता है blockingCollection.CompleteAdding(), तो पहली कॉल को जागना चाहिए और InvalidOperationExceptionगलत उपयोग का प्रतिनिधित्व करने के लिए फेंक देना चाहिए ।

समानांतर एक्सटेंशन में रद्दीकरण


4

यहाँ एक उदाहरण है कि में दो अंक दर्शाता है जवाब से मैक्स Galkin :

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("*********************************************************************");
        Console.WriteLine("* Start canceled task, don't pass token to constructor");
        Console.WriteLine("*********************************************************************");
        StartCanceledTaskTest(false);
        Console.WriteLine();

        Console.WriteLine("*********************************************************************");
        Console.WriteLine("* Start canceled task, pass token to constructor");
        Console.WriteLine("*********************************************************************");
        StartCanceledTaskTest(true);
        Console.WriteLine();

        Console.WriteLine("*********************************************************************");
        Console.WriteLine("* Throw if cancellation requested, don't pass token to constructor");
        Console.WriteLine("*********************************************************************");
        ThrowIfCancellationRequestedTest(false);
        Console.WriteLine();

        Console.WriteLine("*********************************************************************");
        Console.WriteLine("* Throw if cancellation requested, pass token to constructor");
        Console.WriteLine("*********************************************************************");
        ThrowIfCancellationRequestedTest(true);
        Console.WriteLine();

        Console.WriteLine();
        Console.WriteLine("Test Done!!!");
        Console.ReadKey();
    }

    static void StartCanceledTaskTest(bool passTokenToConstructor)
    {
        Console.WriteLine("Creating task");
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        Task task = null;
        if (passTokenToConstructor)
        {
            task = new Task(() => TaskWork(tokenSource.Token, false), tokenSource.Token);

        }
        else
        {
            task = new Task(() => TaskWork(tokenSource.Token, false));
        }

        Console.WriteLine("Canceling task");
        tokenSource.Cancel();

        try
        {
            Console.WriteLine("Starting task");
            task.Start();
            task.Wait();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.Message);
            if (ex.InnerException != null)
            {
                Console.WriteLine("InnerException: {0}", ex.InnerException.Message);
            }
        }

        Console.WriteLine("Task.Status: {0}", task.Status);
    }

    static void ThrowIfCancellationRequestedTest(bool passTokenToConstructor)
    {
        Console.WriteLine("Creating task");
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        Task task = null;
        if (passTokenToConstructor)
        {
            task = new Task(() => TaskWork(tokenSource.Token, true), tokenSource.Token);

        }
        else
        {
            task = new Task(() => TaskWork(tokenSource.Token, true));
        }

        try
        {
            Console.WriteLine("Starting task");
            task.Start();
            Thread.Sleep(100);

            Console.WriteLine("Canceling task");
            tokenSource.Cancel();
            task.Wait();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.Message);
            if (ex.InnerException != null)
            {
                Console.WriteLine("InnerException: {0}", ex.InnerException.Message);
            }
        }

        Console.WriteLine("Task.Status: {0}", task.Status);
    }

    static void TaskWork(CancellationToken token, bool throwException)
    {
        int loopCount = 0;

        while (true)
        {
            loopCount++;
            Console.WriteLine("Task: loop count {0}", loopCount);

            token.WaitHandle.WaitOne(50);
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("Task: cancellation requested");
                if (throwException)
                {
                    token.ThrowIfCancellationRequested();
                }

                break;
            }
        }
    }
}

आउटपुट:

*********************************************************************
* Start canceled task, don't pass token to constructor
*********************************************************************
Creating task
Canceling task
Starting task
Task: loop count 1
Task: cancellation requested
Task.Status: RanToCompletion

*********************************************************************
* Start canceled task, pass token to constructor
*********************************************************************
Creating task
Canceling task
Starting task
Exception: Start may not be called on a task that has completed.
Task.Status: Canceled

*********************************************************************
* Throw if cancellation requested, don't pass token to constructor
*********************************************************************
Creating task
Starting task
Task: loop count 1
Task: loop count 2
Canceling task
Task: cancellation requested
Exception: One or more errors occurred.
InnerException: The operation was canceled.
Task.Status: Faulted

*********************************************************************
* Throw if cancellation requested, pass token to constructor
*********************************************************************
Creating task
Starting task
Task: loop count 1
Task: loop count 2
Canceling task
Task: cancellation requested
Exception: One or more errors occurred.
InnerException: A task was canceled.
Task.Status: Canceled


Test Done!!!
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.