Task.Run () और Task.Factory.StartNew () के बीच क्या अंतर है


191

मेरे पास तरीका है:

private static void Method()
{
    Console.WriteLine("Method() started");

    for (var i = 0; i < 20; i++)
    {
        Console.WriteLine("Method() Counter = " + i);
        Thread.Sleep(500);
    }

    Console.WriteLine("Method() finished");
}

और मैं इस विधि को एक नए कार्य में शुरू करना चाहता हूं। मैं इस तरह नया कार्य शुरू कर सकता हूं

var task = Task.Factory.StartNew(new Action(Method));

या यह

var task = Task.Run(new Action(Method));

लेकिन वहाँ के बीच कोई अंतर है Task.Run()और Task.Factory.StartNew()। टास्क का उदाहरण बनाने के तुरंत बाद दोनों थ्रेडपूल और स्टार्ट विधि () का उपयोग कर रहे हैं। हमें पहले संस्करण का उपयोग कब करना चाहिए और दूसरे का कब?


6
वास्तव में, StartNew को थ्रेडपूल का उपयोग करने की आवश्यकता नहीं है, मेरे उत्तर में जिस ब्लॉग को मैंने जोड़ा है, उसे देखें। समस्या StartNewडिफ़ॉल्ट उपयोगों द्वारा होती है TaskScheduler.Currentजो थ्रेड पूल हो सकता है लेकिन यूआई थ्रेड भी हो सकता है।
स्कॉट चैंबरलेन

जवाबों:


196

दूसरी विधि, Task.Run.NET फ्रेमवर्क के एक बाद के संस्करण (.NET 4.5 में) में पेश की गई है।

हालांकि, पहली विधि, Task.Factory.StartNewआपको उस थ्रेड के बारे में बहुत सारी उपयोगी चीजों को परिभाषित करने का अवसर देती है, जबकि आप बनाना चाहते हैंTask.Run यह प्रदान नहीं करता है।

उदाहरण के लिए, हम कहते हैं कि आप एक लंबे समय तक चलने वाला कार्य सूत्र बनाना चाहते हैं। यदि इस कार्य के लिए थ्रेड पूल का उपयोग किया जा रहा है, तो इसे थ्रेड पूल का दुरुपयोग माना जा सकता है।

इससे बचने के लिए आप एक काम कर सकते हैं, वह यह है कि कार्य को एक अलग धागे में चलाना होगा। एक नया बनाया गया धागा जो इस कार्य के लिए समर्पित होगा और एक बार आपका कार्य पूरा हो जाने के बाद नष्ट हो जाएगा। आप इसे प्राप्त नहींTask.Run कर सकते, जबकि आप Task.Factory.StartNewनीचे के साथ ऐसा कर सकते हैं :

Task.Factory.StartNew(..., TaskCreationOptions.LongRunning);

जैसा कि यहाँ कहा गया है :

तो, .NET फ्रेमवर्क 4.5 डेवलपर पूर्वावलोकन में, हमने नया टास्क शुरू किया है। यह किसी भी तरह से Task.Factory.StartNew का पालन नहीं करता है, बल्कि मापदंडों के एक समूह को निर्दिष्ट करने की आवश्यकता के बिना Task.Factory.StartNew का उपयोग करने के लिए एक त्वरित तरीका के रूप में सोचा जाना चाहिए यह एक शॉर्टकट है। वास्तव में, Task.Run वास्तव में Task.Factory.StartNew के लिए उपयोग किए गए उसी तर्क के संदर्भ में लागू किया गया है, बस कुछ डिफ़ॉल्ट मापदंडों में गुजर रहा है। जब आप कार्य को पास करते हैं।

Task.Run(someAction);

यह इसके बराबर है:

Task.Factory.StartNew(someAction, 
    CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

4
मेरे पास कोड का एक टुकड़ा है जहां स्टेटमेंट that’s exactly equivalent toनहीं है।
एमबोरसा

7
@Emaborsa मैं सराहना करूँगा यदि आप कोड के इस टुकड़े को पोस्ट कर सकते हैं और अपने तर्क को विस्तृत कर सकते हैं। अग्रिम में धन्यवाद !
क्रिस्टोसे

4
@Emaborsa आप एक gist, gist.github.com बना सकते हैं , और इसे साझा कर सकते हैं। हालांकि, इस जिस्ट को साझा करने के अलावा, कृपया निर्दिष्ट करें कि आपको उस परिणाम के लिए कैसे मिला जो वाक्यांश tha's exactly equivalent toधारण नहीं करता है। अग्रिम में धन्यवाद। अपने कोड पर टिप्पणी के साथ समझाना अच्छा होगा। धन्यवाद :)
क्रिस्टोसे

8
यह भी उल्लेखनीय है कि टास्क.ऑन नॅरीस्टेड कार्य डिफ़ॉल्ट रूप से। मैं इस लेख को प्रमुख अंतरों के बारे में पढ़ने की सलाह देता हूं: blogs.msdn.microsoft.com/pfxteam/2011/10/24/…
Pawel Maga

1
@ The0bserver nope, यह है TaskScheduler.Default। कृपया यहाँ एक नज़र देखें reference.microsoft.com/#mscorlib/system/threading/Tasks/…
क्रिस्टोस

46

लोगों ने पहले ही उल्लेख किया है

Task.Run(A);

के बराबर है

Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

लेकिन किसी ने भी इसका उल्लेख नहीं किया

Task.Factory.StartNew(A);

के बराबर है:

Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);

जैसा कि आप देख सकते हैं दो पैरामीटर अलग हैं Task.Runऔर Task.Factory.StartNew:

  1. TaskCreationOptions- Task.Runउपयोग TaskCreationOptions.DenyChildAttachजिसका अर्थ है कि बच्चों के कार्यों को माता-पिता से जोड़ा नहीं जा सकता है, इस पर विचार करें:

    var parentTask = Task.Run(() =>
    {
        var childTask = new Task(() =>
        {
            Thread.Sleep(10000);
            Console.WriteLine("Child task finished.");
        }, TaskCreationOptions.AttachedToParent);
        childTask.Start();
    
        Console.WriteLine("Parent task finished.");
    });
    
    parentTask.Wait();
    Console.WriteLine("Main thread finished.");

    जब हम आह्वान करते हैं parentTask.Wait(), childTaskतो इसका इंतजार नहीं किया जाएगा, भले ही हमने TaskCreationOptions.AttachedToParentइसके लिए निर्दिष्ट किया हो, इसका कारण यह है कि TaskCreationOptions.DenyChildAttachबच्चों को इसे संलग्न करने से मना किया जाता है। आप के साथ एक ही कोड चलाते हैं Task.Factory.StartNewके बजाय Task.Run, parentTask.Wait()के लिए इंतजार करेंगे childTaskक्योंकि Task.Factory.StartNewउपयोग करता हैTaskCreationOptions.None

  2. TaskScheduler- Task.Runउपयोग करता है TaskScheduler.Defaultजिसका अर्थ है कि डिफ़ॉल्ट कार्य अनुसूचक (थ्रेड पूल पर कार्य चलाने वाला) हमेशा कार्यों को चलाने के लिए उपयोग किया जाएगा। Task.Factory.StartNewदूसरी ओर का उपयोग करता है TaskScheduler.Currentजो वर्तमान धागे के अनुसूचक का मतलब है, यह हो सकता है TaskScheduler.Defaultलेकिन हमेशा नहीं। वास्तव में जब विकासशील Winformsया WPFअनुप्रयोगों को चालू थ्रेड से यूआई को अपडेट करने की आवश्यकता होती है, तो ऐसा करने के लिए लोग TaskScheduler.FromCurrentSynchronizationContext()टास्क शेड्यूलर का उपयोग करते हैं , यदि आप अनजाने में कार्य के अंदर एक और लंबे समय तक चलने वाला कार्य बनाते हैं, जो TaskScheduler.FromCurrentSynchronizationContext()यूआई को फ्रीज किया जाएगा। इसका अधिक विस्तृत विवरण यहां पाया जा सकता है

इसलिए आमतौर पर यदि आप नेस्टेड बच्चों के कार्य का उपयोग नहीं कर रहे हैं और हमेशा चाहते हैं कि आपके कार्य थ्रेड पूल पर निष्पादित Task.Runहों तो इसका उपयोग करना बेहतर है , जब तक कि आपके पास कुछ और जटिल परिदृश्य न हों।


1
यह एक शानदार टिप है, स्वीकृत उत्तर होना चाहिए
अली बेअत


28

Task.Runनए नेट ढांचा संस्करण में पेश किया गया है और यह है की सिफारिश की

.NET फ्रेमवर्क 4.5 के साथ शुरू, टास्क.न विधि एक कम्प्यूट-बाउंड कार्य को लॉन्च करने के लिए अनुशंसित तरीका है। स्टार्टअप विधि का उपयोग केवल तब करें जब आपको लंबे समय से चलने वाले, गणना-बाउंड कार्य के लिए ठीक-ठीक नियंत्रण की आवश्यकता हो।

Task.Factory.StartNewअधिक विकल्प हैं, Task.Runएक आशुलिपि है:

रन विधि ओवरलोड का एक सेट प्रदान करता है जो डिफ़ॉल्ट मानों का उपयोग करके कार्य शुरू करना आसान बनाता है। यह StartNew अधिभार का एक हल्का विकल्प है।

और आशुलिपि से मेरा मतलब है एक तकनीकी शॉर्टकट :

public static Task Run(Action action)
{
    return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
        TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
}

21

स्टीफन क्लीरी के इस पोस्ट के अनुसार, Task.Factory.StartNew () खतरनाक है:

मुझे ब्लॉग पर और SO प्रश्नों में बहुत सारे कोड दिखाई देते हैं, जो एक बैकग्राउंड थ्रेड पर कार्य को स्पिन करने के लिए Task.Factory.StartNew का उपयोग करते हैं। स्टीफन टूब के पास एक उत्कृष्ट ब्लॉग लेख है जो बताता है कि Task.Run Task.Factory.StartNew से बेहतर क्यों है, लेकिन मुझे लगता है कि बहुत सारे लोग बस इसे नहीं पढ़ते हैं (या इसे नहीं समझते हैं)। इसलिए, मैंने एक ही तर्क लिया है, कुछ और जबरदस्त भाषा जोड़ी है, और हम देखेंगे कि यह कैसे जाता है। :) StartNew Task.Run की तुलना में कई अधिक विकल्प प्रदान करता है, लेकिन यह काफी खतरनाक है, जैसा कि हम देखेंगे। आपको async कोड में Task.Factory.StartNew पर Task.Run पसंद करना चाहिए।

यहाँ वास्तविक कारण हैं:

  1. Async प्रतिनिधियों को नहीं समझता है। यह वास्तव में बिंदु 1 के समान है क्योंकि आप किन कारणों से StartNew का उपयोग करना चाहते हैं। समस्या यह है कि जब आप StartNew के लिए एक async प्रतिनिधि को पास करते हैं, तो यह मान लेना स्वाभाविक है कि लौटाया गया कार्य उस प्रतिनिधि का प्रतिनिधित्व करता है। हालाँकि, चूंकि Startewew async डेलीगेट्स को नहीं समझता है, वह कार्य वास्तव में किस प्रतिनिधि का प्रतिनिधित्व करता है, यह उस प्रतिनिधि की शुरुआत है। यह उन पहले नुकसानों में से एक है जो एसिंक्स कोड में StartNew का उपयोग करते समय कोडर मुठभेड़ करते हैं।
  2. भ्रामक डिफ़ॉल्ट अनुसूचक। ठीक है, ट्रिक प्रश्न समय: नीचे दिए गए कोड में, "A" विधि किस थ्रेड पर चलती है?
Task.Factory.StartNew(A);

private static void A() { }

ठीक है, तुम्हें पता है कि यह एक चाल सवाल है, एह? यदि आपने उत्तर दिया "थ्रेड पूल थ्रेड", मुझे क्षमा करें, लेकिन यह सही नहीं है। "ए" टास्कस्क्राइडर जो भी वर्तमान में चल रहा है पर चलेगा!

तो इसका मतलब है कि यह संभवतः यूआई थ्रेड पर चल सकता है यदि एक ऑपरेशन पूरा हो जाता है और यह निरंतरता के कारण यूआई थ्रेड में वापस आ जाता है क्योंकि स्टीफन क्लीरी अपने पोस्ट में पूरी तरह से बताते हैं।

मेरे मामले में, मैं एक व्यस्त एनीमेशन प्रदर्शित करते हुए एक दृश्य के लिए डेटाग्रिड लोड करते समय पृष्ठभूमि में कार्यों को चलाने की कोशिश कर रहा था। व्यस्त एनीमेशन का उपयोग करते समय प्रदर्शित नहीं किया गया था, Task.Factory.StartNew()लेकिन जब मैं स्विच किया तो एनीमेशन ठीक से प्रदर्शित हुआ Task.Run()

जानकारी के लिए, कृपया https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html देखें


1

समानताएं अर्थात टास्क.ऑन () टास्क के लिए शॉर्टहैंड होने के अलावा। एफ.शार्टन्यू (), सिंक और एसिंक्स प्रतिनिधियों के मामले में उनके व्यवहार के बीच एक मिनट का अंतर है।

मान लीजिए कि दो तरीके निम्नलिखित हैं:

public async Task<int> GetIntAsync()
{
    return Task.FromResult(1);
}

public int GetInt()
{
    return 1;
}

अब निम्नलिखित कोड पर विचार करें।

var sync1 = Task.Run(() => GetInt());
var sync2 = Task.Factory.StartNew(() => GetInt());

यहाँ Sync1 और Sync2 दोनों प्रकार के टास्क <int> हैं

हालाँकि, async विधियों के मामले में अंतर आता है।

var async1 = Task.Run(() => GetIntAsync());
var async2 = Task.Factory.StartNew(() => GetIntAsync());

इस परिदृश्य में, async1 प्रकार टास्क <int> का है, हालांकि async2 प्रकार टास्क <टास्क <int>> का है


हां, क्योंकि विधि Task.Runकी अलिखित कार्यक्षमता में अंतर्निहित है Unwrapयहाँ एक ब्लॉग पोस्ट है जो इस निर्णय के पीछे के तर्क को स्पष्ट करता है।
थियोडोर जूलियास

-8

मेरे आवेदन में, जो दो सेवाओं को कॉल करता है, मैंने टास्क.न और टास्क दोनों की तुलना की । मैंने पाया कि मेरे मामले में दोनों ठीक काम करते हैं। हालांकि, दूसरा तेज है।


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