কেন টাস্কের ধারাবাহিকতা hen যখন সমস্ত সিঙ্ক্রোনালি কার্যকর করা হয়?


14

Task.WhenAll.NET কোর 3.0 এ চলার সময়, পদ্ধতি সম্পর্কে আমি কেবল একটি কৌতূহলী পর্যবেক্ষণ করেছি । আমি Task.Delayএকক যুক্তি হিসাবে একটি সাধারণ কাজটি Task.WhenAllপেরিয়েছি এবং আমি প্রত্যাশা করেছি যে মোড়ানো টাস্কটি মূল টাস্কের সাথে একই রকম আচরণ করবে। তবে এই ঘটনাটি নয়। মূল টাস্কের ধারাবাহিকতাগুলি অবিচ্ছিন্নভাবে কার্যকর করা হয় (যা পছন্দসই) এবং একাধিক Task.WhenAll(task)মোড়কের ধারাবাহিকতা একের পর এক সিঙ্ক্রোনিকভাবে কার্যকর করা হয় (যা অনাকাঙ্ক্ষিত)।

এই আচরণের একটি ডেমো এখানে । চারটি কর্মী Task.Delayসমাপ্তির জন্য একই কাজটির জন্য অপেক্ষা করছে এবং তারপরে ভারী গণনা (একটি দ্বারা সিমুলেটেড Thread.Sleep) দিয়ে চালিয়ে যান ।

var task = Task.Delay(500);
var workers = Enumerable.Range(1, 4).Select(async x =>
{
    Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}" +
        $" [{Thread.CurrentThread.ManagedThreadId}] Worker{x} before await");

    await task;
    //await Task.WhenAll(task);

    Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}" +
        $" [{Thread.CurrentThread.ManagedThreadId}] Worker{x} after await");

    Thread.Sleep(1000); // Simulate some heavy CPU-bound computation
}).ToArray();
Task.WaitAll(workers);

এখানে আউটপুট। চারটি ধারাবাহিকতা বিভিন্ন থ্রেডে (সমান্তরালে) প্রত্যাশার মতো চলছে।

05:23:25.511 [1] Worker1 before await
05:23:25.542 [1] Worker2 before await
05:23:25.543 [1] Worker3 before await
05:23:25.543 [1] Worker4 before await
05:23:25.610 [4] Worker1 after await
05:23:25.610 [7] Worker2 after await
05:23:25.610 [6] Worker3 after await
05:23:25.610 [5] Worker4 after await

এখন আমি যদি লাইনটি মন্তব্য করি await taskএবং নীচের লাইনে await Task.WhenAll(task)কোনও অসুবিধা না করি তবে আউটপুটটি বেশ আলাদা। সমস্ত ধারাবাহিকতা একই থ্রেডে চলছে, সুতরাং গণনা সমান্তরাল হয় না। প্রতিটি গণনা পূর্ববর্তীটি শেষ হওয়ার পরে শুরু হচ্ছে:

05:23:46.550 [1] Worker1 before await
05:23:46.575 [1] Worker2 before await
05:23:46.576 [1] Worker3 before await
05:23:46.576 [1] Worker4 before await
05:23:46.645 [4] Worker1 after await
05:23:47.648 [4] Worker2 after await
05:23:48.650 [4] Worker3 after await
05:23:49.651 [4] Worker4 after await

আশ্চর্যজনকভাবে এটি তখনই ঘটে যখন প্রতিটি শ্রমিক আলাদা আলাদা মোড়কের জন্য অপেক্ষা করে। যদি আমি মোড়কটিকে সামনে থেকে সংজ্ঞায়িত করি:

var task = Task.WhenAll(Task.Delay(500));

... এবং তারপরে awaitসমস্ত কর্মীদের মধ্যে একই কাজ, আচরণটি প্রথম ক্ষেত্রে (অ্যাসিনক্রোনাস ধারাবাহিকতা) এর মতো।

আমার প্রশ্ন: কেন এমন হচ্ছে? একই কাজটির একই সাথে বিভিন্ন মোড়কের ক্রিয়াকলাপকে একই সুতায় চালিত করার কারণ কী?

দ্রষ্টব্য: একই অদ্ভুত আচরণের ফলাফলের Task.WhenAnyপরিবর্তে কোনও কার্য মোড়ানো Task.WhenAll

অন্য একটি পর্যবেক্ষণ: আমি প্রত্যাশা করেছি যে একটি এর ভিতরে মোড়ক মুড়িয়ে দেওয়া Task.Runধারাবাহিকতাগুলিকে অবিচ্ছিন্ন করে তুলবে। তবে তা হচ্ছে না। নীচের লাইনের ধারাবাহিকতাগুলি এখনও একই থ্রেডে (সমকালীনভাবে) কার্যকর করা হয়।

await Task.Run(async () => await Task.WhenAll(task));

স্পষ্টকরণ: উপরের পার্থক্যগুলি .NET কোর 3.0 প্ল্যাটফর্মে চলমান একটি কনসোল অ্যাপ্লিকেশনটিতে লক্ষ্য করা গেছে। .NET ফ্রেমওয়ার্ক 4.8 এ, মূল টাস্ক বা টাস্ক-র‌্যাপারের অপেক্ষার মধ্যে কোনও পার্থক্য নেই। উভয় ক্ষেত্রে, ধারাবাহিকতা একই থ্রেডে, সুসংগতভাবে কার্যকর করা হয়।


শুধু কৌতূহলী, কী হবে await Task.WhenAll(new[] { task });?
vasily.sib

1
আমি মনে করি এটি এর অভ্যন্তরে সংক্ষিপ্ত প্রদক্ষিণের কারণেTask.WhenAll
মাইকেল র্যান্ডাল

3
লিনকপ্যাড উভয় রূপের জন্য একই প্রত্যাশিত দ্বিতীয় আউটপুট দেয় ... আপনি কোন পরিবেশটি সমান্তরাল রান পেতে ব্যবহার করেন (কনসোল বনাম উইনফোর্ম বনাম ...,। নেট বনাম কোর, ..., ফ্রেমওয়ার্ক সংস্করণ)?
আলেক্সি লেভেনকভ

1
আমি .NET কোর 3.0 এবং 3.1 এই আচরণ নকল করতে সক্ষম হন, কিন্তু প্রাথমিক পরিবর্তন করার পর শুধুমাত্র Task.Delayথেকে 100থেকে 1000এটা সম্পূর্ণ না যাতে যখন awaitইডি।
স্টিফেন ক্লিয়ারি

2
@ ব্লুস্ট্র্যাট সুন্দর সন্ধান! এটি অবশ্যই কোনওভাবে সম্পর্কিত হতে পারে। মজার বিষয় হল আমি .NET ফ্রেমওয়ার্ক 4.6, 4.6.1, 4.7.1, 4.7.2 এবং 4.8 এ মাইক্রোসফ্টের কোডের ভ্রান্ত আচরণ পুনরুত্পাদন করতে ব্যর্থ হয়েছি। আমি প্রতিবার বিভিন্ন থ্রেড আইডি পাই, যা সঠিক আচরণ। এখানে 4.7.2-এ একটি বেহাল দৌড়াচ্ছে।
থিওডর জোউলিয়াস

উত্তর:


2

সুতরাং আপনার কাছে একই টাস্ক ভেরিয়েবলের অপেক্ষায় একাধিক অ্যাসিঙ্ক পদ্ধতি রয়েছে;

    await task;
    // CPU heavy operation

হ্যাঁ, এই ধারাবাহিকতাগুলি taskসম্পূর্ণ হওয়ার পরে সিরিজে ডাকা হবে । আপনার উদাহরণে, প্রতিটি ধারাবাহিকতা পরের দ্বিতীয়টির জন্য থ্রেডটিকে হোগ করে।

আপনি যদি প্রতিটি ধারাবাহিকতা অ্যাসিঙ্ক্রোনালি চালিত করতে চান তবে আপনার মতো কিছু প্রয়োজন হতে পারে;

    await task;
    await Task.Yield().ConfigureAwait(false);
    // CPU heavy operation

যাতে আপনার কাজগুলি প্রাথমিক ধারাবাহিকতা থেকে ফিরে আসে এবং সিপিইউ লোডটি এর বাইরে চলতে দেয় SynchronizationContext


উত্তরের জন্য জেরেমি ধন্যবাদ। হ্যাঁ, Task.Yieldআমার সমস্যার একটি ভাল সমাধান। যদিও আমার প্রশ্নটি এটি কেন ঘটছে সে সম্পর্কে আরও বেশি, এবং কীভাবে পছন্দসই আচরণটি জোর করা যায় সে সম্পর্কে কম।
থিওডর জৌলিয়াস

আপনি যদি সত্যিই জানতে চান তবে উত্স কোডটি এখানে রয়েছে; github.com/microsoft/references Source
জেরেমি

আমি আশা করি যে এটি সহজ ছিল, সম্পর্কিত ক্লাসগুলির উত্স কোড অধ্যয়ন করে আমার প্রশ্নের উত্তর পাওয়া। কোডটি বুঝতে এবং কি হচ্ছে তা নির্ধারণ করতে আমার বয়স হতে হবে!
থিওডর জৌলিয়াস

কীটি এড়ানো হচ্ছে SynchronizationContext, ConfigureAwait(false)মূল টাস্কে একবার কল করা যথেষ্ট।
জেরেমি লেকম্যান

এটি কনসোল অ্যাপ্লিকেশন, এবং এটি SynchronizationContext.Currentশূন্য। তবে আমি নিশ্চিত হয়ে এটি পরীক্ষা করেছি। আমি যোগ ConfigureAwait(false)মধ্যে awaitলাইন এবং এটা কোন পার্থক্য তৈরি। পর্যবেক্ষণগুলি আগের মত একই।
থিওডর জৌলিয়াস

1

যখন কোনও কাজ ব্যবহার করে তৈরি করা হয় Task.Delay(), তার তৈরি বিকল্পগুলি Noneপরিবর্তে সেট করা থাকে RunContinuationsAsychronously

এটি নেট। ফ্রেমওয়ার্ক এবং। নেট কোরের মধ্যে পরিবর্তন ভঙ্গ হতে পারে। তা নির্বিশেষে, আপনি যে আচরণটি পর্যবেক্ষণ করছেন তা ব্যাখ্যা করে এটি উপস্থিত হয় appear এছাড়াও আপনি সোর্স কোড যে মধ্যে খনন থেকে এই যাচাই করতে পারেন Task.Delay()হয় আপ newing একটি DelayPromiseযা ডিফল্টভাবে আহ্বান Taskকন্সট্রাকটর কোন সৃষ্টি অপশন উল্লিখিত রেখে।


উত্তরের জন্য ধন্যবাদ তানভীর সুতরাং আপনি অনুমান করছেন যে .NET কোর অন কোনও নতুন অবজেক্ট তৈরির RunContinuationsAsychronouslyপরিবর্তে ডিফল্ট হয়ে গেছে ? এটি আমার কিছু পর্যবেক্ষণ ব্যাখ্যা করবে তবে সমস্ত নয়। বিশেষত এটি একই র‍্যাপারের অপেক্ষার মধ্যে এবং বিভিন্ন র‍্যাপারগুলির অপেক্ষার মধ্যে পার্থক্যটি ব্যাখ্যা করবে না । NoneTaskTask.WhenAll
থিওডর জোলিয়াস

0

আপনার কোডে, নিম্নলিখিত কোডটি বারবার সংস্থার বাইরে।

var task = Task.Delay(100);

সুতরাং প্রতিবার আপনি নিম্নলিখিতটি চালনার পরে এটি কার্যের অপেক্ষায় থাকবে এবং এটি একটি পৃথক থ্রেডে চালাবে

await task;

তবে আপনি যদি নিম্নলিখিতটি চালনা করেন তবে এটি এর অবস্থা পরীক্ষা করবে task, সুতরাং এটি এটি একটি থ্রেডে চালিত হবে

await Task.WhenAll(task);

তবে আপনি যদি পাশের টাস্ক ক্রিয়েশনটি সরান তবে WhenAllপ্রতিটি টাস্ক আলাদা থ্রেডে চালিত হবে।

var task = Task.Delay(100);
await Task.WhenAll(task);

উত্তরের জন্য Seyedraouf ধন্যবাদ। যদিও আপনার ব্যাখ্যাটি আমার কাছে খুব সন্তোষজনক বলে মনে হচ্ছে না। ফিরে আসা কাজটি আসলটির মতো Task.WhenAllএকটি নিয়মিত । উভয় টাস্ক কোনও সময় সমাপ্ত হয়, টাইমার ইভেন্টের ফলাফল হিসাবে মূল এবং মূল কার্য সমাপ্তির ফলে সম্মিলিত। কেন তাদের ধারাবাহিকতায় আলাদা আচরণ প্রদর্শিত হবে? কোন দিক থেকে একটি কাজ অন্যটির থেকে আলাদা? Tasktask
থিওডর জোলিয়াস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.