c# - অ্যাসিঙ্ক কলটির অপেক্ষায় না থাকা এখনও অ্যাসিঙ্ক, তাই না?




.net asynchronous (2)

আমি দুঃখিত যদি এটি একটি মূর্খ প্রশ্ন হয়

এটি একটি নির্বোধ প্রশ্ন নয়। এটি একটি গুরুত্বপূর্ণ প্রশ্ন।

আমার এক সহকর্মী আছেন যে দাবি করেছেন যে এটি কলটিকে একটি সংক্রামিত করে তোলে এবং তিনি কনসোল.তাই রাইটলাইন লগগুলি নিয়ে আসেন যা আপাতদৃষ্টিতে তার বক্তব্য প্রমাণ করে।

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

যদি আপনার সহকর্মী বা আপনি বিশ্বাস করেন যে অ্যাসিঙ্ক্রোনাস কল হিসাবে এমন কিছু আছে, তবে আপনি ব্যথার জগতে রয়েছেন কারণ কীভাবে অ্যাসিঙ্ক্রোনি কাজ করে সে সম্পর্কে আপনার বিশ্বাস বাস্তবতা থেকে খুব বিচ্ছিন্ন হয়ে যাবে।

সুতরাং, আপনার সহকর্মী সঠিক? অবশ্যই তারা। এটিতে কলটি সিঙ্ক্রোনাস কারণ সমস্ত ফাংশন কলগুলি সমকালীন । তবে সত্য যে তারা বিশ্বাস করে যে "অ্যাসিনক্রোনাস কল" এর মতো একটি জিনিস রয়েছে তার অর্থ তারা সি # তে অ্যাসিক্রোনালি কীভাবে কাজ করে সে সম্পর্কে তারা ভুলভাবে ভুল হয়েছে।

যদি বিশেষত আপনার সহকর্মী বিশ্বাস করেন যে await M() কোনওভাবে M() "অ্যাসিক্রোনাস" করে তোলে, তবে আপনার সহকর্মীর একটি বড় ভুল ধারণা রয়েছে। await অপারেটর is এটি নিশ্চিত করার জন্য এটি একটি জটিল অপারেটর, তবে এটি একটি অপারেটর এবং এটি মানগুলির উপর পরিচালিত হয়। await M() এবং var t = M(); await t; var t = M(); await t; একই জিনিস । প্রতীক্ষাটি কল হওয়ার পরে ঘটে কারণ await যে মানটি ফিরে আসে তার উপর পরিচালিত হয় await সংকলককে "এম () তে অ্যাসিক্রোনাস কল উত্পন্ন করার" বা এই জাতীয় কোনও নির্দেশ নয়; "অ্যাসিনক্রোনাস কল" বলে কোনও জিনিস নেই।

যদি এটি তাদের মিথ্যা বিশ্বাসের প্রকৃতি হয়, তবে আপনি await অর্থ কী তা আপনার সহকর্মীকে শিক্ষিত করার সুযোগ পেয়েছেন। await অর্থ সাধারণ কিছু কিন্তু শক্তিশালী। এর অর্থ:

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

এটাই await । এটি কেবল একটি কার্যের বিষয়বস্তুগুলি পরীক্ষা করে, এবং যদি কাজটি অসম্পূর্ণ হয়, তবে এটি বলে যে "ভাল, কাজটি শেষ না হওয়া পর্যন্ত আমরা এই ওয়ার্কফ্লোতে কোনও অগ্রগতি করতে পারি না, সুতরাং আমার এই আহ্বানকের কাছে ফিরে যান যিনি এই সিপিইউর জন্য অন্য কিছু খুঁজে পাবেন to করতে".

এ এর কোডটির প্রকৃতি পরিবর্তন হয় না কারণ আমরা এটির জন্য অপেক্ষা করি না।

এটাই সঠিক. আমরা সিঙ্ক্রোনালি এটিকে কল করি এবং এটি একটি Task । কল সাইটের পরে কোডটি এ না আসা পর্যন্ত চলবে না। এ সম্পর্কিত আকর্ষণীয় বিষয়টি হ'ল এটিকে তার কলারের কাছে একটি অসম্পূর্ণ Task ফেরত দেওয়ার অনুমতি দেওয়া হয় এবং সেই কাজটি একটি অ্যাসিনক্রোনাস ওয়ার্কফ্লোতে নোডকে উপস্থাপন করে। কর্মপ্রবাহটি ইতিমধ্যে অবিচ্ছিন্ন, এবং আপনি লক্ষ্য করেছেন যে এটি ফিরে আসার পরে তার ফেরতের মান দিয়ে আপনি যা করেন তা এটার কোনও তাত্পর্যপূর্ণ হয় না; আপনি ফিরে আসা Task await যাচ্ছেন কিনা সে বিষয়ে কোনও ধারণা নেই। A যতক্ষণ পারে এটি চালায় এবং তারপরে হয় এটি একটি সম্পূর্ণ-সাধারন কার্য, বা একটি সম্পূর্ণ-ব্যতিক্রমী টাস্ক দেয় বা এটি একটি অসম্পূর্ণ কার্যকে ফিরিয়ে দেয়। কিন্তু কল সাইটে আপনি যে কিছুই করেন না তা পরিবর্তিত হয়।

যেহেতু এ থেকে ফেরতের মান প্রয়োজন হয় না, তাই কল সাইটে টাস্কের জন্য অপেক্ষা করার দরকার নেই

সঠিক।

কল সাইটে এই কাজের জন্য অপেক্ষা করার দরকার নেই, যতক্ষণ না চেইন আপ কেউ অপেক্ষায় থাকে (যা সিতে ঘটে)।

এখন আপনি আমাকে হারিয়েছেন কাউকে কেন A দ্বারা ফেরত Task জন্য অপেক্ষা করতে হবে? বলুন আপনি কেন বিশ্বাস করেন যে কারও কাছে সেই Task জন্য await করা আবশ্যক , কারণ আপনার ভ্রান্ত বিশ্বাস থাকতে পারে।

আমার সহকর্মী খুব জেদী এবং আমি নিজেকে সন্দেহ করতে শুরু করি। আমার বোঝা কি ভুল?

আপনার সহকর্মী প্রায় অবশ্যই ভুল। আপনার বিশ্লেষণটি ঠিক ঠিক তেমন ঠিক মনে হয় যেখানে আপনি বলে থাকেন যে প্রতিটি Task await প্রয়োজন , যা সত্য নয়। কোনও Task await না করা await করা কারণ এটির অর্থ হ'ল আপনি এমন একটি প্রোগ্রাম লিখেছিলেন যেখানে আপনি কোনও অপারেশন শুরু করেছিলেন এবং কখন বা কীভাবে এটি সম্পন্ন হবে সে সম্পর্কে কোনও চিন্তা নেই এবং এটি অবশ্যই এইরকম একটি প্রোগ্রাম লিখতে খারাপ গন্ধ পেয়েছে , তবে প্রয়োজন নেই is প্রতিটি Task জন্য await আপনি যদি বিশ্বাস করেন যে আবারও আছে, সেই বিশ্বাসটি কী তা বলুন এবং আমরা এটিকে বাছাই করব।

আমি দুঃখিত যদি এটি একটি মূর্খ প্রশ্ন (বা সদৃশ) হয়।

আমার একটি ফাংশন রয়েছে:

public async Task<int> A(/* some parameters */)
{
    var result = await SomeOtherFuncAsync(/* some other parameters */);

    return (result);
}

আমি আর একটি ফাংশন B , এ কল করে কিন্তু রিটার্ন মান ব্যবহার না করে:

public Task B(/* some parameters */)
{
    var taskA = A(/* parameters */); // #1

    return (taskA);
}

নোট করুন যে B async ঘোষিত হয়নি এবং A কলটির অপেক্ষায় নেই। A কলটি আগুন এবং ভুলে যাওয়া কল নয় - B কে C মাধ্যমে ডাকা হয়:

public async Task C()
{
    await B(/* parameters */);
}

লক্ষ করুন যে # 1 এ , await । আমার এক সহকর্মী আছেন যে দাবি করেছেন যে এটি কলটিকে একটি সংক্রামিত করে তোলে এবং তিনি Console.WriteLine লগগুলি নিয়ে আসেন যা আপাতদৃষ্টিতে তার বক্তব্য প্রমাণ করে।

আমি এটি উল্লেখ করার চেষ্টা করেছি যে কেবলমাত্র আমরা B অভ্যন্তরের ফলাফলের জন্য অপেক্ষা করি না, টাস্ক চেইনটি অপেক্ষা করা হয় এবং এ এর ​​অভ্যন্তরের কোডের প্রকৃতি পরিবর্তন হয় না কারণ আমরা এটির জন্য অপেক্ষা করি না। যেহেতু এ থেকে রিটার্ন মান প্রয়োজন হয় না, কল সাইটে কার্যের জন্য অপেক্ষা করার প্রয়োজন নেই, যতক্ষণ না চেইন আপ কেউ এটির জন্য অপেক্ষা করে (যা C ঘটে)।

আমার সহকর্মী খুব জেদী এবং আমি নিজেকে সন্দেহ করতে শুরু করি। আমার বোঝা কি ভুল?


তুমি ঠিক. কোনও টাস্ক তৈরি করা কেবল এটিই করে এবং কখন এবং কে এর ফলাফলের জন্য অপেক্ষা করবে তা বিবেচ্য নয়। await Task.Delay(veryBigNumber); চেষ্টা করুন। await Task.Delay(veryBigNumber); SomeOtherFuncAsync এবং কনসোল আউটপুটটি আপনার প্রত্যাশা মতো হওয়া উচিত।

এটিকে এলিডিং বলা হয় এবং আমি আপনাকে এই ব্লগপোস্টটি পড়ার পরামর্শ দিই, যেখানে আপনি কেন এমন জিনিস করা উচিত বা না করা উচিত তা দেখতে পারেন can

এছাড়াও কিছু ন্যূনতম (সামান্য বিভ্রান্তিকর) উদাহরণ যা আপনার কোডটি আপনাকে সঠিক প্রমাণ করে দিচ্ছে:

class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine($"Start of main {Thread.CurrentThread.ManagedThreadId}");
            var task = First();
            Console.WriteLine($"Middle of main {Thread.CurrentThread.ManagedThreadId}");
            await task;
            Console.WriteLine($"End of main {Thread.CurrentThread.ManagedThreadId}");
        }

        static Task First()
        {
            return SecondAsync();
        }

        static async Task SecondAsync()
        {
            await ThirdAsync();
        }

        static async Task ThirdAsync()
        {
            Console.WriteLine($"Start of third {Thread.CurrentThread.ManagedThreadId}");
            await Task.Delay(1000);
            Console.WriteLine($"End of third {Thread.CurrentThread.ManagedThreadId}");
        }
    }

এটি Middle of main লিখেছেন End of third আগে, এটি প্রমাণ করে যে এটি আসলে অ্যাসিনক্রোনাস। Furhtermore আপনি (সম্ভবত সম্ভবত) দেখতে পারেন যে ফাংশনগুলির শেষগুলি প্রোগ্রামের বাকি অংশের চেয়ে বিভিন্ন থ্রেডে চালিত হয়। শুরু এবং প্রধানের মাঝের উভয়ই সর্বদা একই থ্রেডে চলবে কারণ এগুলি আসলে সংশ্লেষপূর্ণ (মূল সূচনা হয়, ফাংশন চেইনকে কল করে, তৃতীয় রিটার্ন (এটি await কীওয়ার্ডের সাথে লাইনে ফিরে আসতে পারে)) এবং তারপরে মূলটি এমনভাবে চলতে থাকে যেন সেখানে থাকে এর আগে কোনও অ্যাসিঙ্ক্রোনাস ফাংশন জড়িত ছিল না both উভয় ফাংশনের অপেক্ষার কীওয়ার্ডগুলির শেষে থ্রেডপুলের কোনও থ্রেডে চলতে পারে (বা আপনি ব্যবহার করছেন এমন সিঙ্ক্রোনাইজেশন প্রসঙ্গে)।

এখন এটি লক্ষণীয় আকর্ষণীয়, Third Task.Delay যদি খুব বেশি সময় না নেয় এবং প্রকৃতপক্ষে Task.Delay হয়, তবে Task.Delay সমস্ত একক থ্রেডে চলবে। আরও কী, যদিও এটি অবিচ্ছিন্নভাবে চলবে, এটি সমস্ত একক থ্রেডে চালিত হতে পারে । এখানে কোনও নিয়ম নেই যে একটি অ্যাসিঙ্ক ফাংশন একাধিক থ্রেড ব্যবহার করবে, কিছু I / O টাস্ক শেষ হওয়ার অপেক্ষায় এটি খুব ভালভাবে অন্য কিছু কাজ করতে পারে।





async-await