टास्क शुरू नहीं करने का ऐलान कैसे किया जाएगा जो एक और टास्क के लिए इंतजार करेगा?


9

मैंने यह यूनिट टेस्ट कर लिया है और मुझे समझ नहीं आ रहा है कि "टास्क का इंतजार क्यों करें।" () "इंतजार नहीं करता!"

   [TestMethod]
    public async Task SimpleTest()
    {
        bool isOK = false;
        Task myTask = new Task(async () =>
        {
            Console.WriteLine("Task.BeforeDelay");
            await Task.Delay(1000);
            Console.WriteLine("Task.AfterDelay");
            isOK = true;
            Console.WriteLine("Task.Ended");
        });
        Console.WriteLine("Main.BeforeStart");
        myTask.Start();
        Console.WriteLine("Main.AfterStart");
        await myTask;
        Console.WriteLine("Main.AfterAwait");
        Assert.IsTrue(isOK, "OK");
    }

यहां यूनिट टेस्ट आउटपुट है:

यूनिट टेस्ट आउटपुट

यह कैसे संभव है एक "प्रतीक्षा" प्रतीक्षा नहीं करता है, और मुख्य धागा जारी है?


यह थोड़ा अस्पष्ट है कि आप क्या हासिल करने की कोशिश कर रहे हैं। क्या आप कृपया अपना अपेक्षित आउटपुट जोड़ सकते हैं?
ओलेग्वी

1
परीक्षण विधि बहुत स्पष्ट है - isOK के सच होने की उम्मीद है
सर Rufo

गैर-प्रारंभ किए गए कार्य को बनाने का कोई कारण नहीं है। कार्य धागे नहीं हैं, वे धागे का उपयोग करते हैं । तुम क्या करने की कोशिश कर रहे हो? Task.Run()पहले के बाद उपयोग क्यों नहीं Console.WriteLine?
पनगीओटीस कानावोस

1
@ यदि आपने केवल थ्रेडपूल का वर्णन किया है। आप नहीं है एक नौकरी कतार लागू करने के लिए कार्यों की एक पूल की जरूरत है। आपको नौकरियों की एक कतार की आवश्यकता है, उदाहरण के लिए एक्शन <टी> ऑब्जेक्ट्स
पनगीओटीस कानावोस

2
@ आप जो करने का प्रयास करते हैं वह पहले से ही .NET में उपलब्ध है। जैसे कि TPL डेटाफ़्लो कक्षाओं जैसे एक्शनब्लॉक, या नए System.Threading.Channels क्लासेस के माध्यम से। आप एक या अधिक समवर्ती कार्यों का उपयोग करके संदेशों को प्राप्त करने और संसाधित करने के लिए एक ActionBlock बना सकते हैं। सभी ब्लॉकों में विन्यास क्षमता वाले इनपुट बफ़र्स हैं। डीओपी और क्षमता आपको समवर्ती, थ्रॉटल अनुरोधों को नियंत्रित करने और
बैकस्पेस को

जवाबों:


8

new Task(async () =>

एक कार्य एक नहीं लेता है Func<Task>, लेकिन एक Action। यह आपके एसिंक्रोनस तरीके को कॉल करेगा और जब यह वापस आएगा, तो इसे समाप्त होने की उम्मीद है। लेकिन ऐसा नहीं है। यह एक कार्य देता है। वह कार्य नए कार्य द्वारा प्रतीक्षित नहीं है। नए कार्य के लिए, विधि लौटाए जाने के बाद कार्य किया जाता है।

आपको उस कार्य का उपयोग करने की आवश्यकता है जो पहले से ही एक नए कार्य में लपेटने के बजाय मौजूद है:

[TestMethod]
public async Task SimpleTest()
{
    bool isOK = false;

    Func<Task> asyncMethod = async () =>
    {
        Console.WriteLine("Task.BeforeDelay");
        await Task.Delay(1000);
        Console.WriteLine("Task.AfterDelay");
        isOK = true;
        Console.WriteLine("Task.Ended");
    };

    Console.WriteLine("Main.BeforeStart");
    Task myTask = asyncMethod();

    Console.WriteLine("Main.AfterStart");

    await myTask;
    Console.WriteLine("Main.AfterAwait");
    Assert.IsTrue(isOK, "OK");
}

4
Task.Run(async() => ... )यह भी एक विकल्प है
सर रूफो

आपने बस प्रश्न के लेखक के रूप में ही किया था
ओलेग्वी

बीटीडब्ल्यू myTask.Start();InvalidOperationException
सर रूफो

@ मैं इसे नहीं देखता। क्या आप इसे समझा सकते हैं?
9

@nvoigt मुझे लगता है कि उनका मतलब है कि myTask.Start()उनके विकल्प के लिए एक अपवाद होगा और उपयोग करते समय हटा दिया जाना चाहिए Task.Run(...)। आपके समाधान में कोई त्रुटि नहीं है।
404

3

समस्या यह है कि आप गैर-जेनेरिक Taskवर्ग का उपयोग कर रहे हैं , जिसका अर्थ परिणाम उत्पन्न करना नहीं है। तो जब आप Taskएक async डेलीगेट पास करने वाले उदाहरण को बनाते हैं :

Task myTask = new Task(async () =>

... प्रतिनिधि के रूप में माना जाता है async void। एकasync void नहीं है Task, यह इंतजार नहीं किया जा सकता है, इसके अपवाद को संभाला नहीं जा सकता है, और यह हज़ारों सवालों का एक स्रोत है जो स्टैकऑवरफ़्लो और अन्य जगहों पर यहाँ निराश हैं। समाधान सामान्य Task<TResult>वर्ग का उपयोग करना है, क्योंकि आप एक परिणाम वापस करना चाहते हैं, और परिणाम एक और है Task। इसलिए आपको एक बनाना होगा Task<Task>:

Task<Task> myTask = new Task<Task>(async () =>

अब जब आप Startबाहरी Task<Task>होंगे तो यह लगभग तुरंत पूरा हो जाएगा क्योंकि इसका काम सिर्फ आंतरिक निर्माण करना है Task। फिर आपको भीतर की भी प्रतीक्षा करनी होगी Task। यह इस प्रकार किया जा सकता है:

myTask.Start();
Task myInnerTask = await myTask;
await myInnerTask;

आपके पास दो विकल्प हैं। यदि आपको आंतरिक संदर्भ की स्पष्ट आवश्यकता नहीं है, Taskतो आप केवल Task<Task>दो बार बाहरी प्रतीक्षा कर सकते हैं :

await await myTask;

... या आप बिल्ट-इन एक्सटेंशन विधि का उपयोग कर सकते हैं Unwrapजो बाहरी और आंतरिक कार्यों को एक में जोड़ती है:

await myTask.Unwrap();

जब आप बहुत अधिक लोकप्रिय का उपयोग करते हैं तो यह अलिखित स्वचालित रूप से होता है Task.Run विधि का जो गर्म कार्य बनाता है, इसलिए Unwrapआजकल इसका उपयोग बहुत अधिक नहीं किया जाता है।

यदि आप यह तय करते हैं कि आपके async प्रतिनिधि को परिणाम वापस करना होगा, उदाहरण के लिए a string , तो आपको myTaskचर को प्रकार का घोषित करना चाहिए Task<Task<string>>

नोट: मैं Taskठंडे कार्यों को बनाने के लिए निर्माणकर्ताओं के उपयोग का समर्थन नहीं करता । एक अभ्यास के रूप में आम तौर पर उन कारणों के लिए, जिन्हें मैं वास्तव में नहीं जानता हूं, लेकिन शायद ही कभी इसका उपयोग किया जाता है, क्योंकि यह आश्चर्य से कोड के अन्य अनजान उपयोगकर्ताओं / अनुरक्षकों / समीक्षकों को पकड़ने की क्षमता रखता है।

सामान्य सलाह: हर बार सावधान रहें कि आप किसी विधि के तर्क के रूप में एक एसिंक्स प्रतिनिधि को आपूर्ति कर रहे हैं। इस विधि से आदर्श रूप से एक Func<Task>तर्क की उम्मीद की जानी चाहिए (जिसका अर्थ है कि async प्रतिनिधियों को समझता है), या कम से कम एक Func<T>तर्क (जिसका अर्थ है कि कम से कम उत्पन्न Taskको अनदेखा नहीं किया जाएगा)। दुर्भाग्यपूर्ण मामले में कि यह विधि एक को स्वीकार करती है Action, आपके प्रतिनिधि को माना जाएगा async void। यह शायद ही कभी आप चाहते हैं, अगर है।


कैसे एक अलग तकनीकी जवाब! धन्यवाद।
एलो

@ मेरी ख़ुशी!
थियोडोर ज़ूलियास

1
 [Fact]
        public async Task SimpleTest()
        {
            bool isOK = false;
            Task myTask = new Task(() =>
            {
                Console.WriteLine("Task.BeforeDelay");
                Task.Delay(3000).Wait();
                Console.WriteLine("Task.AfterDelay");
                isOK = true;
                Console.WriteLine("Task.Ended");
            });
            Console.WriteLine("Main.BeforeStart");
            myTask.Start();
            Console.WriteLine("Main.AfterStart");
            await myTask;
            Console.WriteLine("Main.AfterAwait");
            Assert.True(isOK, "OK");
        }

यहां छवि विवरण दर्ज करें


3
आपको लगता है कि awaitकार्य में देरी के बिना , वास्तव में उस कार्य में देरी नहीं होगी, है ना? आपने कार्यक्षमता को हटा दिया।
nvoigt

मैंने अभी-अभी परीक्षण किया, @nvoigt सही है: बीता हुआ समय: 0: 00: 00,0106554। और हम आपके स्क्रीनशॉट में देखते हैं: "बीता हुआ समय: 18 एमएस", यह होना चाहिए> = 1000 एमएस
एलो

हां, आप सही हैं, मैं अपनी प्रतिक्रिया अपडेट करता हूं। Tnx। आप टिप्पणियों के बाद मैं इसे कम से कम परिवर्तनों के साथ हल करता हूं। :)
बास्का

1
प्रतीक्षा का उपयोग करने के लिए अच्छा विचार () और टास्क के अंदर से इंतजार नहीं करना चाहिए! धन्यवाद !
एलो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.