সঠিকভাবে যখন Task.Run ব্যবহার করুন এবং যখন ঠিক async-প্রতীক্ষা করুন


317

আমি কখন আপনাকে ব্যবহার করতে হবে তার সঠিক আর্কিটেকচার সম্পর্কে আপনার মতামত জানতে চাই Task.Run। আমি আমাদের ডাব্লুপিএফ। নেট 4.5 অ্যাপ্লিকেশনটিতে (ক্যালিবার্ন মাইক্রো কাঠামোর সাথে) ল্যাগি ইউআই অনুভব করছি।

মূলত আমি করছি (খুব সরল কোড স্নিপেটস):

public class PageViewModel : IHandle<SomeMessage>
{
   ...

   public async void Handle(SomeMessage message)
   {
      ShowLoadingAnimation();

      // Makes UI very laggy, but still not dead
      await this.contentLoader.LoadContentAsync();

      HideLoadingAnimation();
   }
}

public class ContentLoader
{
    public async Task LoadContentAsync()
    {
        await DoCpuBoundWorkAsync();
        await DoIoBoundWorkAsync();
        await DoCpuBoundWorkAsync();

        // I am not really sure what all I can consider as CPU bound as slowing down the UI
        await DoSomeOtherWorkAsync();
    }
}

আমি যে নিবন্ধগুলি / ভিডিওগুলি পড়েছি / দেখেছি তা থেকে, আমি জানি যে await asyncঅগত্যা কোনও পটভূমির থ্রেডে চলছে না এবং পটভূমিতে কাজ শুরু করার জন্য আপনাকে এটি অপেক্ষা করে মোড়ানো প্রয়োজন Task.Run(async () => ... )। ব্যবহার করা async awaitUI কে অবরুদ্ধ করে না, তবুও এটি ইউআই থ্রেডটিতে চলছে তাই এটি এটিকে পিছিয়ে দিচ্ছে।

টাস্ক.রুন রাখার সেরা জায়গাটি কোথায়?

আমি ঠিক করা উচিত

  1. বাইরের কলটি মোড়ানো কারণ এটি .NET এর জন্য থ্রেডিংয়ের কাজ কম

  2. , বা আমার কেবলমাত্র সিপিইউযুক্ত পদ্ধতিগুলি অভ্যন্তরীণভাবে চালানো উচিত যা Task.Runএটি অন্য জায়গাগুলির জন্য পুনরায় ব্যবহারযোগ্য করে তোলে? আমি এখানে নিশ্চিত নই যে মূলত গভীর পটভূমির থ্রেডগুলিতে কাজ শুরু করা ভাল ধারণা।

বিজ্ঞাপন (1), প্রথম সমাধানটি এরকম হবে:

public async void Handle(SomeMessage message)
{
    ShowLoadingAnimation();
    await Task.Run(async () => await this.contentLoader.LoadContentAsync());
    HideLoadingAnimation();
}

// Other methods do not use Task.Run as everything regardless
// if I/O or CPU bound would now run in the background.

বিজ্ঞাপন (2), দ্বিতীয় সমাধানটি এরকম হবে:

public async Task DoCpuBoundWorkAsync()
{
    await Task.Run(() => {
        // Do lot of work here
    });
}

public async Task DoSomeOtherWorkAsync(
{
    // I am not sure how to handle this methods -
    // probably need to test one by one, if it is slowing down UI
}

বিটিডাব্লু, (1) এর লাইনটি await Task.Run(async () => await this.contentLoader.LoadContentAsync());সহজভাবে হওয়া উচিত await Task.Run( () => this.contentLoader.LoadContentAsync() );। আফাইক আপনি দ্বিতীয় awaitএবং asyncভিতরে যোগ করে কিছু অর্জন করতে পারেন না Task.Run। এবং যেহেতু আপনি প্যারামিটারগুলি অতিক্রম করছেন না, এটি কিছুটা আরও সরল করে await Task.Run( this.contentLoader.LoadContentAsync );
টুলমেকারস্টেভ

আপনার যদি দ্বিতীয় অভ্যন্তরে অপেক্ষা করতে হয় তবে কিছুটা পার্থক্য রয়েছে। এই নিবন্ধটি দেখুন । আমি এটি খুব দরকারী বলে মনে করেছি, কেবলমাত্র এই নির্দিষ্ট পয়েন্টের সাথে আমি সম্মত নই এবং অপেক্ষা করার পরিবর্তে সরাসরি টাস্কে ফিরে যাওয়া পছন্দ করি। (আপনি যেমন আপনার মন্তব্যে পরামর্শ দিয়েছেন)
লুকাশ কে

উত্তর:


365

আমার ব্লগে সংগৃহীত ইউআই থ্রেডে কাজ করার জন্য নির্দেশিকা নোট করুন :

  • একবারে 50 মাইলের বেশি ইউআই থ্রেড ব্লক করবেন না।
  • আপনি প্রতি সেকেন্ডে ইউআই থ্রেডে 100 ডলার ধারাবাহিকতা নির্ধারণ করতে পারেন; 1000 অনেক বেশি।

আপনার দুটি কৌশল ব্যবহার করা উচিত:

1) ConfigureAwait(false)আপনি যখন পারেন ব্যবহার করুন ।

যেমন, await MyAsync().ConfigureAwait(false);পরিবর্তে await MyAsync();

ConfigureAwait(false)আপনাকে জানায় awaitযে আপনার বর্তমান প্রসঙ্গে পুনরায় সূচনা করার দরকার নেই (এই ক্ষেত্রে, "বর্তমান প্রসঙ্গে" মানে "ইউআই থ্রেডে")। তবে, সেই asyncপদ্ধতিটির বাকি অংশের জন্য (এর পরে ConfigureAwait), আপনি এমন কিছু করতে পারবেন না যা ধরে নেবে যে আপনি বর্তমান প্রসঙ্গে রয়েছেন (যেমন, ইউআই উপাদানগুলি আপডেট করুন)।

আরও তথ্যের জন্য, আমার এমএসডিএন নিবন্ধটি অ্যাসিঙ্ক্রোনাস প্রোগ্রামিংয়ের সেরা অভ্যাসগুলি দেখুন

2) Task.Runসিপিইউযুক্ত পদ্ধতিতে কল করতে ব্যবহার করুন ।

আপনার ব্যবহার করা উচিত Task.Run, তবে যে কোনও কোডের মধ্যে আপনি পুনরায় ব্যবহারযোগ্য হতে চান (যেমন, গ্রন্থাগার কোড) নয়। সুতরাং আপনি পদ্ধতিটি কল করার Task.Runজন্য ব্যবহার করেন, পদ্ধতিটি বাস্তবায়নের অংশ হিসাবে নয় ।

সুতরাং বিশুদ্ধরূপে সিপিইউযুক্ত কাজটি দেখতে এরকম হবে:

// Documentation: This method is CPU-bound.
void DoWork();

যাকে আপনি ব্যবহার করে কল করবেন Task.Run:

await Task.Run(() => DoWork());

যে পদ্ধতিগুলি সিপিইউ-বাউন্ডের মিশ্রণ এবং আই / ও-বাউন্ডের Asyncসিপিইউ-বাউন্ড প্রকৃতিটি নির্দেশ করে ডকুমেন্টেশন সহ একটি স্বাক্ষর থাকতে হবে:

// Documentation: This method is CPU-bound.
Task DoWorkAsync();

যা ব্যবহার করে Task.Runআপনিও কল করবেন (যেহেতু এটি আংশিক সিপিইউ-আবদ্ধ):

await Task.Run(() => DoWorkAsync());

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

12
আপনার সমস্ত গ্রন্থাগার পদ্ধতি ব্যবহার করা উচিত ConfigureAwait(false)। আপনি যদি প্রথমে এটি করেন তবে আপনি এটি Task.Runসম্পূর্ণ অপ্রয়োজনীয় দেখতে পাবেন । যদি আপনার এখনও প্রয়োজন হয় Task.Run, তবে আপনি একবার বা বহুবার কল করেছেন কিনা তা এই ক্ষেত্রে রানটাইমের সাথে খুব বেশি পার্থক্য রাখে না, তাই আপনার কোডের জন্য সবচেয়ে প্রাকৃতিক জিনিসটিই করুন।
স্টিফেন ক্লিয়ারি

2
আমি বুঝতে পারি না প্রথম কৌশলটি কীভাবে তাকে সাহায্য করবে। এমনকি আপনি যদি ConfigureAwait(false)আপনার সিপিইউ-বাউন্ড পদ্ধতিতে ব্যবহার করেন তবে এটি এখনও ইউআই থ্রেড যা সিপিইউ-আবদ্ধ পদ্ধতিটি করতে চলেছে, এবং কেবলমাত্র সমস্ত কিছু টিপি থ্রেডে সম্পন্ন হতে পারে। নাকি আমি কিছু ভুল বুঝেছি?
দারিয়াস

4
@ ব্যবহারকারী4205580: না, অ্যাসিনক্রোনাস Task.Runস্বাক্ষরগুলি বোঝে, সুতরাং এটি সম্পূর্ণ না হওয়া পর্যন্ত DoWorkAsyncশেষ হবে না । অতিরিক্ত async/ awaitঅপ্রয়োজনীয় আমি শিষ্টাচার নিয়ে আমার ব্লগ সিরিজেরTask.Run আরও "কেন" ব্যাখ্যা করি ।
স্টিফেন ক্লিয়ারি

3
@ user4205580: না "" কোর "বিস্তৃত অ্যাসিঙ্ক পদ্ধতিগুলি অভ্যন্তরীণভাবে এটি ব্যবহার করে না। স্বাভাবিক একটি "কোর" ASYNC পদ্ধতি বাস্তবায়ন উপায় ব্যবহার করা TaskCompletionSource<T>বা এই ধরনের তার সাধারণভাবে সংক্ষেপে স্বরলিপি এক FromAsync। আমার একটি ব্লগ পোস্ট রয়েছে যা অ্যাসিঙ্ক পদ্ধতিতে থ্রেডের প্রয়োজন হয় না কেন তা আরও বিশদে যায়
স্টিফেন ক্লিয়ারি

12

আপনার ContentLoader এর একটি সমস্যা হ'ল অভ্যন্তরীণভাবে এটি ক্রমানুসারে পরিচালিত হয়। আরও ভাল প্যাটার্নটি হল কাজের সমান্তরাল করা এবং তারপরে শেষে সিঙ্ক্রোনাইজ করা, যাতে আমরা পাই

public class PageViewModel : IHandle<SomeMessage>
{
   ...

   public async void Handle(SomeMessage message)
   {
      ShowLoadingAnimation();

      // makes UI very laggy, but still not dead
      await this.contentLoader.LoadContentAsync(); 

      HideLoadingAnimation();   
   }
}

public class ContentLoader 
{
    public async Task LoadContentAsync()
    {
        var tasks = new List<Task>();
        tasks.Add(DoCpuBoundWorkAsync());
        tasks.Add(DoIoBoundWorkAsync());
        tasks.Add(DoCpuBoundWorkAsync());
        tasks.Add(DoSomeOtherWorkAsync());

        await Task.WhenAll(tasks).ConfigureAwait(false);
    }
}

স্পষ্টতই, যদি কোনও কাজের জন্য পূর্বের অন্যান্য কার্যগুলির ডেটা প্রয়োজন হয় তবে এটি কাজ করে না, তবে বেশিরভাগ পরিস্থিতিতে আপনাকে আরও সামগ্রিকভাবে থ্রুপুট দিতে হবে।

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