কেন আমি একাধিক প্রতীক্ষার চেয়ে একক 'অপেক্ষা করণীয় টাস্ক.কখন সব' পছন্দ করব?


127

যদি আমি টাস্ক সমাপ্তির ক্রমটির বিষয়ে চিন্তা না করি এবং কেবল তাদের সমস্তটি সম্পন্ন করা দরকার, তখনও কি আমি একাধিকের await Task.WhenAllপরিবর্তে ব্যবহার করব await? উদাহরণস্বরূপ, (এবং কেন?) এর DoWork2কাছে একটি পছন্দের পদ্ধতির নীচে রয়েছে DoWork1:

using System;
using System.Threading.Tasks;

namespace ConsoleApp
{
    class Program
    {
        static async Task<string> DoTaskAsync(string name, int timeout)
        {
            var start = DateTime.Now;
            Console.WriteLine("Enter {0}, {1}", name, timeout);
            await Task.Delay(timeout);
            Console.WriteLine("Exit {0}, {1}", name, (DateTime.Now - start).TotalMilliseconds);
            return name;
        }

        static async Task DoWork1()
        {
            var t1 = DoTaskAsync("t1.1", 3000);
            var t2 = DoTaskAsync("t1.2", 2000);
            var t3 = DoTaskAsync("t1.3", 1000);

            await t1; await t2; await t3;

            Console.WriteLine("DoWork1 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
        }

        static async Task DoWork2()
        {
            var t1 = DoTaskAsync("t2.1", 3000);
            var t2 = DoTaskAsync("t2.2", 2000);
            var t3 = DoTaskAsync("t2.3", 1000);

            await Task.WhenAll(t1, t2, t3);

            Console.WriteLine("DoWork2 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
        }


        static void Main(string[] args)
        {
            Task.WhenAll(DoWork1(), DoWork2()).Wait();
        }
    }
}

2
সমান্তরালভাবে আপনাকে কতগুলি কাজ করতে হবে তা যদি আপনি আসলে না জানেন? আপনার যদি 1000 টি কাজ চালানোর দরকার হয় তবে কী হবে? প্রথমটি খুব বেশি পঠনযোগ্য হবে না await t1; await t2; ....; await tn=> দ্বিতীয়টি সর্বদা উভয় ক্ষেত্রেই সেরা পছন্দ
কুওং

আপনার মন্তব্যটি অর্থবোধ করে। আমি সম্প্রতি নিজের উত্তর দেওয়া অন্য প্রশ্নের সাথে সম্পর্কিত নিজের জন্য কিছু স্পষ্ট করার চেষ্টা করছিলাম । সেক্ষেত্রে 3 টি কাজ ছিল।
এড়ানো

উত্তর:


113

হ্যাঁ, ব্যবহার করুন WhenAllকারণ এটি একবারে সমস্ত ত্রুটি প্রচার করে। একাধিক প্রতীক্ষার সাথে, যদি পূর্বের কোনও একটি নিক্ষেপ করে তবে আপনি ত্রুটিগুলি হারাবেন।

আর একটি গুরুত্বপূর্ণ পার্থক্য হ'ল ব্যর্থতার উপস্থিতিতেও (ত্রুটিযুক্ত বা বাতিল কার্যাদি) WhenAllসমস্ত কার্য সম্পন্ন হওয়ার জন্য অপেক্ষা করবে । ক্রমানুসারে ম্যানুয়ালি অপেক্ষা করা অপ্রত্যাশিত সম্মিলিত কারণ হতে পারে কারণ আপনার প্রোগ্রামটির যে অংশটি অপেক্ষা করতে চায় সেটি আসলে তাড়াতাড়ি চালিয়ে যাবে।

আমি মনে করি এটি কোড পড়াও সহজ করে দেয় কারণ আপনি যে শব্দার্থকতা চান সেটি সরাসরি কোডে নথিভুক্ত।


9
"কারণ এটি একবারে সমস্ত ত্রুটি প্রচার করে" আপনি যদি awaitএর ফলাফল না হন।
এসভিক

2
টাস্কের সাথে কীভাবে ব্যতিক্রমগুলি পরিচালিত হয় সে সম্পর্কে এই প্রশ্নটি এই নিবন্ধটি এর পিছনে যুক্তিগুলিকে একটি দ্রুত কিন্তু ভাল অন্তর্দৃষ্টি দেয় (এবং ঠিক তাই ঘটে যখন একাধিক প্রত্যাশার বিপরীতে হ'লএল এর সুবিধাগুলির একটি উত্তীর্ণ নোটও তৈরি করা হয়): ব্লগ .msdn.com / বি / পিএফএক্সটিয়াম / আর্কাইভ / 2011/09/28 / 10217876.aspx
লিন্ডবার্গ

5
@ অসকারলাইন্ডবার্গ ওপিতে প্রথমটির অপেক্ষার আগেই তিনি সমস্ত কাজ শুরু করছেন। সুতরাং তারা একযোগে চালানো। লিঙ্কের জন্য ধন্যবাদ।
usr

3
@ অাস আমি তখনও জানতে আগ্রহী ছিলাম যে, যখন সমস্ত একই সিঙ্ক্রোনাইজেশন কনটেক্সট সংরক্ষণের মতো চতুর কাজ না করে, এর বেনিফিটগুলিকে শব্দার্থবিজ্ঞান থেকে দূরে সরিয়ে রাখার জন্য। আমি কোনও চূড়ান্ত ডকুমেন্টেশন পাইনি, তবে আইএলটির দিকে তাকালে স্পষ্টতই আইএআইএনসিএনস্টেটম্যাচিনের বিভিন্ন নাটক রয়েছে। আমি সমস্ত ভাল আইএল পড়ি না, তবে খুব কম সময়ে সমস্তগুলি আরও কার্যকর আইএল কোড তৈরি করে বলে মনে হয়। (যাই হোক না কেন, একাকী সত্য যে যখন সমস্ত ফলাফল আমার সাথে জড়িত সমস্ত কাজকর্মের অবস্থা প্রতিফলিত করে তবে বেশিরভাগ ক্ষেত্রে এটিকে অগ্রাধিকার দেওয়া যথেষ্ট কারণ।)
ওসকার লিন্ডবার্গ

17
আর একটি গুরুত্বপূর্ণ পার্থক্য হ'ল যখন সমস্ত কর্ম সমস্ত কাজ শেষ হওয়ার জন্য অপেক্ষা করবে, এমনকি, উদাহরণস্বরূপ, টি 1 বা টি 2 ব্যতিক্রম ছুঁড়ে ফেলেছে বা বাতিল হয়ে গেছে।
ম্যাগনাস

28

আমার বোঝাটি হ'ল Task.WhenAllএকাধিক awaitসকে প্রাধান্য দেওয়ার মূল কারণটি হল পারফরম্যান্স / টাস্ক "মন্থন": DoWork1পদ্ধতিটি এরকম কিছু করে:

  • প্রদত্ত প্রসঙ্গে শুরু করুন
  • প্রসঙ্গ সংরক্ষণ করুন
  • টি 1 এর জন্য অপেক্ষা করুন
  • আসল প্রসঙ্গটি পুনরুদ্ধার করুন
  • প্রসঙ্গ সংরক্ষণ করুন
  • t2 জন্য অপেক্ষা করুন
  • আসল প্রসঙ্গটি পুনরুদ্ধার করুন
  • প্রসঙ্গ সংরক্ষণ করুন
  • t3 জন্য অপেক্ষা করুন
  • আসল প্রসঙ্গটি পুনরুদ্ধার করুন

বিপরীতে, এটি DoWork2করে:

  • প্রদত্ত প্রসঙ্গে শুরু করুন
  • প্রসঙ্গ সংরক্ষণ করুন
  • টি 1, টি 2 এবং টি 3 এর জন্য অপেক্ষা করুন
  • আসল প্রসঙ্গটি পুনরুদ্ধার করুন

আপনার নির্দিষ্ট ক্ষেত্রে এটি যথেষ্ট বড় চুক্তি কিনা তা অবশ্যই "প্রসঙ্গ নির্ভর" (শোধকে ক্ষমা করে দেওয়া)।


4
আপনি মনে করছেন যে তিনি একটি সিঙ্ক্রোনাইজেশন প্রসঙ্গে একটি বার্তা পাঠানো ব্যয়বহুল। এটা আসলে না। আপনার একটি প্রতিনিধি রয়েছে যা একটি কাতারে যুক্ত হয়, সেই সারিটি পড়তে হবে এবং প্রতিনিধি কার্যকর করা হবে। এটি যুক্ত করে ওভারহেড সত্যিই খুব ছোট। এটি কিছুই নয়, তবে এটি বড়ও নয়। অ্যাসিঙ্ক অপারেশনগুলি যা-ই হোক না কেন এর ব্যয় প্রায় সমস্ত ক্ষেত্রে যেমন ওভারহেড বামন করবে।
পরিবেশন করুন

একমত, এটি কেবলমাত্র এক কারণ যা আমি অন্যের চেয়ে বেশি পছন্দ করতে পারি। ওয়েল, টাস্ক.ওয়েটএল এর সাথে একই মিল, যেখানে থ্রেড স্যুইচিং একটি আরও উল্লেখযোগ্য ব্যয়।
মার্সেল পপেস্কু

1
@ সার্ভে যেমন মার্সেল উল্লেখ করেছেন যে সত্যই নির্ভর করে। যদি আপনি উদাহরণস্বরূপ নীতি বিষয় হিসাবে সমস্ত ডিবি কাজের জন্য অপেক্ষা করেন, এবং সেই ডিবি যেমন মেশিনে বসে থাকে এসপ নেট উদাহরণ হিসাবে, আপনি এমন একটি ডিবি হিটের অপেক্ষায় রয়েছেন যা মেমরির ইন-ইনডেক্স, সস্তা che সিঙ্ক্রোনাইজেশন সুইচ এবং থ্রেডপুলের সাফল্যের চেয়ে এটি। এই ধরণের দৃশ্যে হ্যাভলএল () এর সাথে একটি উল্লেখযোগ্য সামগ্রিক জয় হতে পারে, সুতরাং ... এটি সত্যই নির্ভর করে।
ক্রিস মোসচিনি

3
@ ক্রিসমোসচিনি এমন কোনও উপায় নেই যে সার্ভারের মতো একই মেশিনে বসে কোনও ডিবিকে আঘাত করলেও কোনও বার্তা পাম্পে কয়েকজন প্রতিনিধি যুক্ত করার ওভারহেডের চেয়ে দ্রুত গতিতে চলেছে ডিবি কোয়েরি query সেই ইন-মেমরি ক্যোয়ারীটি এখনও প্রায় অবশ্যই অনেক ধীর হতে চলেছে।
পরিবেশন করুন

আরও মনে রাখবেন যে যদি টি 1 ধীর হয় এবং টি 2 এবং টি 3 দ্রুত হয় - তবে অন্যটি অবিলম্বে ফিরে আসার অপেক্ষায় রয়েছে।
ডেভিড রেফেলি

18

একটি অ্যাসিক্রোনাস পদ্ধতিটি রাষ্ট্র-মেশিন হিসাবে প্রয়োগ করা হয়। পদ্ধতিগুলি রচনা করা সম্ভব যাতে সেগুলি রাষ্ট্র-মেশিনে সংকলিত না হয়, এটি প্রায়শই একটি দ্রুত ট্র্যাক অ্যাসিঙ্ক পদ্ধতি হিসাবে উল্লেখ করা হয়। এগুলি এর মতো প্রয়োগ করা যেতে পারে:

public Task DoSomethingAsync()
{
    return DoSomethingElseAsync();
}

যখন ব্যবহার Task.WhenAllএটা এখনও আহ্বানকারী নিশ্চিত সব কর্ম সম্পন্ন করা জন্য অপেক্ষা করতে, যেমন সক্ষম হয় এই ফাস্ট ট্রাক কোড বজায় রাখা সম্ভব হল:

public Task DoSomethingAsync()
{
    var t1 = DoTaskAsync("t2.1", 3000);
    var t2 = DoTaskAsync("t2.2", 2000);
    var t3 = DoTaskAsync("t2.3", 1000);

    return Task.WhenAll(t1, t2, t3);
}

7

(বিশেষ দ্রষ্টব্য: এই উত্তরটি নেওয়া হয় / ইয়ান গ্রিফিথস 'TPL এসিঙ্ক থেকে অনুপ্রাণিত উপর কোর্স Pluralsight )

ওয়েলএলকে পছন্দ করার অন্য একটি কারণ হ'ল ব্যতিক্রম হ্যান্ডলিং।

ধরুন আপনার ডুওর্ক পদ্ধতিতে আপনার ট্রাই-ক্যাচ ব্লক ছিল এবং ধরুন তারা বিভিন্ন ডটাস্ক পদ্ধতিতে কল করছে:

static async Task DoWork1() // modified with try-catch
{
    try
    {
        var t1 = DoTask1Async("t1.1", 3000);
        var t2 = DoTask2Async("t1.2", 2000);
        var t3 = DoTask3Async("t1.3", 1000);

        await t1; await t2; await t3;

        Console.WriteLine("DoWork1 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
    }
    catch (Exception x)
    {
        // ...
    }

}

এই ক্ষেত্রে, যদি সমস্ত 3 টি কার্য ব্যতিক্রম ছুঁড়ে ফেলে, তবে কেবল প্রথমটি ধরা পড়বে। পরবর্তী কোনও ব্যতিক্রম হারিয়ে যাবে। উদাহরণস্বরূপ, যদি টি 2 এবং টি 3 ব্যতিক্রম ছুঁড়ে ফেলে তবে কেবল টি 2 কে ধরা হবে; ইত্যাদি পরবর্তী কাজগুলি ব্যতিক্রমগুলি অরক্ষিত থাকবে।

যেখানে উইলএল হিসাবে - যেখানে কোনও বা সমস্ত কার্য ত্রুটিযুক্ত, ফলস্বরূপ কার্যটিতে সমস্ত ব্যতিক্রম থাকবে। অপেক্ষার কীওয়ার্ডটি এখনও সর্বদা প্রথম ব্যতিক্রমটিকে পুনরায় নিক্ষেপ করে। সুতরাং অন্যান্য ব্যতিক্রমগুলি এখনও কার্যকরভাবে অরক্ষিত। এটি থেকে উত্তরণের একটি উপায় হ'ল সমস্ত কাজের পরে খালি ধারাবাহিকতা যুক্ত করা এবং সেখানে অপেক্ষাটি করা। এইভাবে যদি কাজটি ব্যর্থ হয়, ফলাফলের সম্পত্তি সম্পূর্ণ সমষ্টি ব্যতিক্রম ছুঁড়ে ফেলবে:

static async Task DoWork2() //modified to catch all exceptions
{
    try
    {
        var t1 = DoTask1Async("t1.1", 3000);
        var t2 = DoTask2Async("t1.2", 2000);
        var t3 = DoTask3Async("t1.3", 1000);

        var t = Task.WhenAll(t1, t2, t3);
        await t.ContinueWith(x => { });

        Console.WriteLine("DoWork1 results: {0}", String.Join(", ", t.Result[0], t.Result[1], t.Result[2]));
    }
    catch (Exception x)
    {
        // ...
    }
}

6

এই প্রশ্নের অন্যান্য উত্তর কেন await Task.WhenAll(t1, t2, t3);অগ্রাধিকার দেওয়া হয় তার প্রযুক্তিগত কারণগুলি উপস্থাপন করে । এখনও এই একই সিদ্ধান্তে পৌঁছানোর সময় এই উত্তরটির লক্ষ্য এটি আরও নরম দিক থেকে (যা @ অ্যাসার ইঙ্গিত দেয়) থেকে দেখবে।

await Task.WhenAll(t1, t2, t3); এটি একটি আরও কার্যকরী পন্থা, কারণ এটি উদ্দেশ্য হিসাবে ঘোষণা করে এবং এটি পারমাণবিক।

সঙ্গে await t1; await t2; await t3;, সেখানে পৃথক মধ্যে কোড যোগ করা থেকে কোনো সতীর্থ (অথবা এমনকি আপনার ভবিষ্যৎ স্ব!) প্রতিরোধ কিছুই নয় awaitবিবৃতি। অবশ্যই, আপনি এটি প্রয়োজনীয়তার সাথে সম্পন্ন করার জন্য এটি একটি লাইনে সংকুচিত করেছেন, তবে এটি সমস্যার সমাধান করে না। এছাড়াও, একটি নির্দিষ্ট কোডের লাইনটিতে একাধিক বিবৃতি অন্তর্ভুক্ত করার জন্য একটি দল সেটিংয়ে এটি সাধারণত খারাপ ফর্ম, কারণ এটি মানুষের চোখের স্ক্যান করতে উত্স ফাইলটিকে আরও শক্ত করে তুলতে পারে।

সহজ ভাষায় বলা যায়, await Task.WhenAll(t1, t2, t3);এটি আরও রক্ষণাবেক্ষণযোগ্য, কারণ এটি আপনার উদ্দেশ্যকে আরও স্পষ্টভাবে যোগাযোগ করে এবং অদ্ভুত বাগগুলির পক্ষে কম ঝুঁকির মধ্যে থাকে যা কোডের সার্থক আপডেটগুলি থেকে বেরিয়ে আসতে পারে, এমনকি কেবল মার্জগুলিও ভুল হয়ে গেছে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.