"অপেক্ষা করুন কার্যের মধ্যে কোনও পার্থক্য। রুন (); প্রত্যাবর্তন এবং "রিটার্ন টাস্ক। রুন ()"?


90

নিম্নলিখিত দুটি টুকরা কোডের মধ্যে কোনও ধারণাগত পার্থক্য রয়েছে:

async Task TestAsync() 
{
    await Task.Run(() => DoSomeWork());
}

এবং

Task TestAsync() 
{
    return Task.Run(() => DoSomeWork());
}

উত্পন্ন কোডটি কি আলাদা হয়?

সম্পাদনা:Task.Run অনুরূপ কেস নিয়ে বিভ্রান্তি এড়াতে :

async Task TestAsync() 
{
    await Task.Delay(1000);
}

এবং

Task TestAsync() 
{
    return Task.Delay(1000);
}

শেষ আপডেট: গৃহীত উত্তর ছাড়াও, কীভাবে LocalCallContextপরিচালনা করা হয় তার মধ্যেও একটি পার্থক্য রয়েছে : কল কনটেক্সট L লজিক্যাল গেটডেটা যেখানে অ্যাসিঙ্ক্রোনী নেই সেখানে পুনরুদ্ধার হয়। কেন?


4
হ্যাঁ, এটি পৃথক। এবং এটি অনেক পৃথক। না হলে await/ ব্যবহার করার কোনও মানে asyncহবে না :)
মার্সিনজুরাসেক

4
আমার মনে হয় এখানে দুটি প্রশ্ন আছে। 1. পদ্ধতিটির প্রকৃত বাস্তবায়ন কি তার কলারের পক্ষে? ২. দুটি পদ্ধতির সংকলিত উপস্থাপনা কী আলাদা?
ডেভিডআরআর

উত্তর:


80

একটি প্রধান পার্থক্য ব্যতিক্রম প্রচার হয়। ব্যতিক্রম, একটি ভিতরে নিহিত async Taskপদ্ধতি, ফিরে সঞ্চিত পরার Taskবস্তু এবং সুপ্ত থাকে যতক্ষণ না কাজটি মাধ্যমে পালিত পরার await task, task.Wait(), task.Resultবা task.GetAwaiter().GetResult()। পদ্ধতির সিঙ্ক্রোনাস অংশ থেকে ফেলে দেওয়া হলেও এটি এইভাবে প্রচার করা হয় async

নিম্নলিখিত কোডটি বিবেচনা করুন, যেখানে OneTestAsyncএবং AnotherTestAsyncবেশ আলাদাভাবে আচরণ করে:

static async Task OneTestAsync(int n)
{
    await Task.Delay(n);
}

static Task AnotherTestAsync(int n)
{
    return Task.Delay(n);
}

// call DoTestAsync with either OneTestAsync or AnotherTestAsync as whatTest
static void DoTestAsync(Func<int, Task> whatTest, int n)
{
    Task task = null;
    try
    {
        // start the task
        task = whatTest(n);

        // do some other stuff, 
        // while the task is pending
        Console.Write("Press enter to continue");
        Console.ReadLine();
        task.Wait();
    }
    catch (Exception ex)
    {
        Console.Write("Error: " + ex.Message);
    }
}

যদি আমি কল করি DoTestAsync(OneTestAsync, -2)তবে এটি নিম্নলিখিত আউটপুট উত্পাদন করে:

চালিয়ে যাওয়ার জন্য এন্টার টিপুন
ত্রুটি: এক বা একাধিক ত্রুটি ঘটেছে awa
ত্রুটি: 2 য়

দ্রষ্টব্য, Enterএটি দেখতে আমাকে টিপতে হয়েছিল।

এখন, যদি আমি কল করি DoTestAsync(AnotherTestAsync, -2), তবে কোড ওয়ার্কফ্লো ভিতরে DoTestAsync। এবার আমাকে টিপতে বলা হয়নি Enter:

ত্রুটি: মানটি -1 হওয়া উচিত (অসীম সময়সীমা নির্দেশ করে), 0 বা ধনাত্মক পূর্ণসংখ্যা।
প্যারামিটারের নাম: মিলিসেকেন্ডসডেলিআরার: 1 ম

উভয় ক্ষেত্রে Task.Delay(-2)এর পরামিতিগুলি যাচাই করার সময় শুরুতে ছুড়ে দেয়। এটি একটি Task.Delay(1000)অন্তর্নিহিত দৃশ্য হতে পারে, তবে তাত্ত্বিকভাবে খুব সম্ভবত ফেলে দিতে পারে, উদাহরণস্বরূপ, যখন অন্তর্নিহিত সিস্টেমের টাইমার এপিআই ব্যর্থ হয়।

পার্শ্ব নোটে, ত্রুটি প্রচারের যুক্তি async voidপদ্ধতিগুলির জন্য এখনও পৃথক ( পদ্ধতিগুলির বিপরীতে async Task)। কোনও async voidপদ্ধতির অভ্যন্তরে উত্থাপিত একটি ব্যতিক্রম তাত্ক্ষণিকভাবে বর্তমান থ্রেডের সিঙ্ক্রোনাইজেশন প্রসঙ্গে (মাধ্যমে SynchronizationContext.Post) SynchronizationContext.Current != null)পুনরায় নিক্ষেপ করা হবে , যদি বর্তমান থ্রেডটিতে একটি থাকে ( অন্যথায়, এটি পুনরায় নিক্ষেপ করা হবে ThreadPool.QueueUserWorkItem)। কলারের কাছে একই স্ট্যাক ফ্রেমে এই ব্যতিক্রমটি পরিচালনা করার সুযোগ নেই।

আমি TPL ব্যতিক্রম হ্যান্ডলিং আচরণ সম্পর্কে আরো কিছু বিবরণ পোস্ট এখানে এবং এখানে


প্রশ্ন : asyncঅ-অ্যাসিঙ্ক Taskভিত্তিক পদ্ধতিগুলির জন্য ব্যতিক্রমী প্রচারের পদ্ধতিগুলি কী নকল করা সম্ভব , যাতে পরবর্তীকর্তারা একই স্ট্যাক ফ্রেমের উপর না ফেলে?

উত্তর : যদি সত্যিই প্রয়োজন হয়, তবে হ্যাঁ, এর জন্য একটি কৌশল আছে:

// async
async Task<int> MethodAsync(int arg)
{
    if (arg < 0)
        throw new ArgumentException("arg");
    // ...
    return 42 + arg;
}

// non-async
Task<int> MethodAsync(int arg)
{
    var task = new Task<int>(() => 
    {
        if (arg < 0)
            throw new ArgumentException("arg");
        // ...
        return 42 + arg;
    });

    task.RunSynchronously(TaskScheduler.Default);
    return task;
}

তবে নোট করুন, কিছু শর্তের মধ্যে (যেমন এটি স্ট্যাকের খুব গভীর তখন) RunSynchronouslyতবুও তাত্পর্যপূর্ণভাবে কার্যকর করতে পারে।


আরেকটি উল্লেখযোগ্য পার্থক্য যে / সংস্করণ আরো মৃত লক একটি অ-ডিফল্ট সিঙ্ক্রোনাইজেশন প্রসঙ্গের উপর প্রবণ । উদাহরণস্বরূপ, নিম্নলিখিতগুলিতে একটি উইনফোর্ডস বা ডাব্লুপিএফ অ্যাপ্লিকেশনটিতে মৃত-লক হবে:asyncawait

static async Task TestAsync()
{
    await Task.Delay(1000);
}

void Form_Load(object sender, EventArgs e)
{
    TestAsync().Wait(); // dead-lock here
}

এটিকে একটি অ্যাসিঙ্ক সংস্করণে পরিবর্তন করুন এবং এটি মৃত-লক হবে না:

Task TestAsync() 
{
    return Task.Delay(1000);
}

মৃত-লকটির প্রকৃতিটি তার ব্লগে স্টিফেন ক্লিয়ারি দ্বারা ভালভাবে ব্যাখ্যা করা হয়েছে ।


4
আমি বিশ্বাস করি যে প্রথম উদাহরণে অচলাবস্থাটি অপেক্ষার লাইনে .ConfigureAwait (মিথ্যা) যোগ করে এড়ানো সম্ভব, কারণ এটি কেবল তখন ঘটে কারণ পদ্ধতিটি একই মৃত্যুদন্ড প্রসঙ্গে ফিরে যাওয়ার চেষ্টা করছে। সুতরাং exeptions শুধুমাত্র পার্থক্য যে অবশেষ।
তুলনামূলকভাবে_রান্দম

4
@ রিলেটিভলি_আরেন্ডম, আপনার মন্তব্যটি সঠিক, যদিও উত্তরটি ছিল এর পরিবর্তে return Task.Run()এবং এর মধ্যে পার্থক্য সম্পর্কেawait Task.Run(); returnawait Task.Run().ConfigureAwait(false); return
নসরটিটিও

আপনি যদি এন্টার চাপার পরে প্রোগ্রামটি বন্ধ হয়ে যায়, আপনি এফ 5 এর পরিবর্তে সিটিআরএল + এফ 5 করছেন তা নিশ্চিত করুন।
ডেভিড ক্লেম্পফনার

54

পার্থক্য কি

async Task TestAsync() 
{
    await Task.Delay(1000);
}

এবং

Task TestAsync() 
{
    return Task.Delay(1000);
}

?

আমি এই প্রশ্ন দ্বারা বিভ্রান্ত। আমাকে অন্য প্রশ্নের সাথে আপনার প্রশ্নের জবাব দিয়ে পরিষ্কার করার চেষ্টা করি। এর মধ্যে পার্থক্য কী?

Func<int> MakeFunction()
{
    Func<int> f = ()=>1;
    return ()=>f();
}

এবং

Func<int> MakeFunction()
{
    return ()=>1;
}

?

আমার দুটি জিনিসের মধ্যে পার্থক্য যাই হোক না কেন, আপনার দুটি জিনিসের মধ্যে একই পার্থক্য।


23
অবশ্যই! আপনি আমার চোখ খুলে রেখেছেন :) প্রথম ক্ষেত্রে, আমি একটি মোড়ক টাস্ক তৈরি করি, শব্দার্থগতভাবে কাছে Task.Delay(1000).ContinueWith(() = {})। দ্বিতীয়, এটা ঠিক এর Task.Delay(1000)। পার্থক্যটি কিছুটা সূক্ষ্ম, তবে তাৎপর্যপূর্ণ।
10:38

4
আপনি কিছুটা পার্থক্য ব্যাখ্যা করতে পারেন? আসলে আমি তা করি না .. ধন্যবাদ
ইয়ু

4
সিঙ্ক প্রসঙ্গে একটি সূক্ষ্ম পার্থক্য দেওয়া হয়েছে, এবং ব্যতিক্রম প্রচার আমি বলব async / প্রতীক্ষা এবং ফাংশন র‌্যাপারগুলির মধ্যে পার্থক্য এক নয়।
ক্যামেরন ম্যাকফারল্যান্ড

4
@ ক্যামেরনম্যাকফারল্যান্ড: এ কারণেই আমি স্পষ্টতা চেয়েছিলাম। প্রশ্নটি জিজ্ঞাসা করে যে উভয়ের মধ্যে একটি ধারণাগত পার্থক্য রয়েছে। ভাল, আমি জানি না। অবশ্যই অনেক পার্থক্য আছে; তাদের মধ্যে কি "ধারণাগত" পার্থক্য হিসাবে গণনা করা হয়? নেস্টেড ফুনাক্স সহ আমার উদাহরণে ত্রুটি প্রচারের ক্ষেত্রেও পার্থক্য রয়েছে; ফাংশনগুলি যদি স্থানীয় রাজ্যের উপর বন্ধ থাকে তবে স্থানীয় জীবনকালগুলির মধ্যে পার্থক্য রয়েছে এবং এই জাতীয় কিছু। এই "ধারণাগত" পার্থক্য কি?
এরিক লিপার্ট

6
এটি একটি পুরানো উত্তর, তবে আমি বিশ্বাস করি যে আজ দেওয়া হয়েছে, তা হ্রাস পেয়েছে। এটি প্রশ্নের উত্তর দেয় না, না এটি ওপিকে এমন কোনও উত্সকে নির্দেশ করে যা থেকে সে শিখতে পারে।
ড্যানিয়েল দুবভস্কি

11
  1. প্রথম পদ্ধতিটিও সংকলন করে না।

    যেহেতু ' Program.TestAsync()' একটি অ্যাসিঙ্ক পদ্ধতি যা প্রত্যাবর্তন করে ' Task', তাই কোনও প্রত্যাবর্তন কীওয়ার্ড অবশ্যই অবজেক্ট এক্সপ্রেশন দ্বারা অনুসরণ করা উচিত নয়। তুমি কি ' Task<T>' ফেরার ইচ্ছা করেছ ?

    এটা করা হয়েছে

    async Task TestAsync()
    {
        await Task.Run(() => DoSomeWork());
    }
    
  2. এই দুটিয়ের মধ্যে প্রধান ধারণাগত পার্থক্য রয়েছে। প্রথমটি অ্যাসিক্রোনাস, দ্বিতীয়টি নয়। অ্যাসিঙ্ক পারফরম্যান্স পড়ুন:async / এর ইন্টার্নালগুলি সম্পর্কে আরও কিছু জানতে এসাইক এবং অ্যাওয়েটের ব্যয়গুলি বোঝাawait

  3. তারা বিভিন্ন কোড জেনারেট করে না।

    .method private hidebysig 
        instance class [mscorlib]System.Threading.Tasks.Task TestAsync () cil managed 
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = (
            01 00 25 53 4f 54 65 73 74 50 72 6f 6a 65 63 74
            2e 50 72 6f 67 72 61 6d 2b 3c 54 65 73 74 41 73
            79 6e 63 3e 64 5f 5f 31 00 00
        )
        .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x216c
        // Code size 62 (0x3e)
        .maxstack 2
        .locals init (
            [0] valuetype SOTestProject.Program/'<TestAsync>d__1',
            [1] class [mscorlib]System.Threading.Tasks.Task,
            [2] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder
        )
    
        IL_0000: ldloca.s 0
        IL_0002: ldarg.0
        IL_0003: stfld class SOTestProject.Program SOTestProject.Program/'<TestAsync>d__1'::'<>4__this'
        IL_0008: ldloca.s 0
        IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create()
        IL_000f: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder'
        IL_0014: ldloca.s 0
        IL_0016: ldc.i4.m1
        IL_0017: stfld int32 SOTestProject.Program/'<TestAsync>d__1'::'<>1__state'
        IL_001c: ldloca.s 0
        IL_001e: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder'
        IL_0023: stloc.2
        IL_0024: ldloca.s 2
        IL_0026: ldloca.s 0
        IL_0028: call instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start<valuetype SOTestProject.Program/'<TestAsync>d__1'>(!!0&)
        IL_002d: ldloca.s 0
        IL_002f: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder SOTestProject.Program/'<TestAsync>d__1'::'<>t__builder'
        IL_0034: call instance class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task()
        IL_0039: stloc.1
        IL_003a: br.s IL_003c
    
        IL_003c: ldloc.1
        IL_003d: ret
    } // end of method Program::TestAsync
    

    এবং

    .method private hidebysig 
        instance class [mscorlib]System.Threading.Tasks.Task TestAsync2 () cil managed 
    {
        // Method begins at RVA 0x21d8
        // Code size 23 (0x17)
        .maxstack 2
        .locals init (
            [0] class [mscorlib]System.Threading.Tasks.Task CS$1$0000
        )
    
        IL_0000: nop
        IL_0001: ldarg.0
        IL_0002: ldftn instance class [mscorlib]System.Threading.Tasks.Task SOTestProject.Program::'<TestAsync2>b__4'()
        IL_0008: newobj instance void class [mscorlib]System.Func`1<class [mscorlib]System.Threading.Tasks.Task>::.ctor(object, native int)
        IL_000d: call class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Threading.Tasks.Task::Run(class [mscorlib]System.Func`1<class [mscorlib]System.Threading.Tasks.Task>)
        IL_0012: stloc.0
        IL_0013: br.s IL_0015
    
        IL_0015: ldloc.0
        IL_0016: ret
    } // end of method Program::TestAsync2
    

@ মার্সিন জুরাসেক, সত্যিই এটি সংকলন হয়নি। এটি একটি টাইপো ছিল, আমি নিশ্চিত যে আপনি এটি ঠিক পেয়েছেন। অন্যথায়, দুর্দান্ত উত্তর, ধন্যবাদ! আমি ভেবেছিলাম প্রথম ক্ষেত্রে স্টেট মেশিন ক্লাস তৈরি করা এড়াতে সি # যথেষ্ট স্মার্ট হতে পারে।
এজেড

9

দুটি উদাহরণ না পৃথক। যখন কোনও পদ্ধতিটি asyncকীওয়ার্ডের সাথে চিহ্নিত করা হয় , তখন সংকলক পর্দার আড়ালে একটি রাষ্ট্র-মেশিন তৈরি করে। প্রত্যাশিত প্রত্যাশার পরে এটি ধারাবাহিকতা পুনরায় শুরু করার জন্য দায়ী।

বিপরীতে, যখন কোনও পদ্ধতি আপনার সাথে চিহ্নিত নাasync হয় তখন অপেক্ষা করার ক্ষমতা awaitহারাতে থাকে। (এটি হ'ল পদ্ধতিটির মধ্যেই; পদ্ধতিটি এখনও তার কলার দ্বারা অপেক্ষা করা যেতে পারে,) তবে asyncকীওয়ার্ডটি এড়িয়ে আপনি আর স্টেট-মেশিন তৈরি করতে পারবেন না, যা বেশিরভাগ ওভারহেড যোগ করতে পারে (স্থানীয়দেরকে ক্ষেত্রগুলিতে তুলতে পারে) রাষ্ট্র-মেশিনের, জিসিতে অতিরিক্ত বস্তু)।

এর মতো উদাহরণগুলিতে আপনি যদি async-awaitসরাসরি প্রত্যাশিত এড়াতে এবং ফিরিয়ে দিতে সক্ষম হন তবে পদ্ধতির দক্ষতা উন্নত করার জন্য এটি করা উচিত।

দেখুন এই প্রশ্নের এবং এই উত্তরটি যা খুবই আপনার প্রশ্ন এবং উত্তর একই রকম।

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