c# - عند استخدام Task.Run بشكل صحيح وعند انتظار async فقط




asynchronous async-await (2)

لاحظ الإرشادات الخاصة بتنفيذ العمل على مؤشر ترابط واجهة المستخدم ، التي تم جمعها على مدونتي:

  • لا تحظر مؤشر ترابط واجهة المستخدم لأكثر من 50 مللي ثانية في كل مرة.
  • يمكنك جدولة ~ 100 متابعة على مؤشر ترابط واجهة المستخدم في الثانية ؛ 1000 هو أكثر من اللازم.

هناك طريقتان يجب عليك استخدامهما:

1) استخدم ConfigureAwait(false) عندما تستطيع.

Eg، await MyAsync().ConfigureAwait(false); بدلا من await MyAsync(); .

يخبر ConfigureAwait(false) await أن لا تحتاج إلى استئناف في السياق الحالي (في هذه الحالة ، "في السياق الحالي" تعني "على مؤشر ترابط واجهة المستخدم"). ومع ذلك ، بالنسبة لبقية طريقة async (بعد ConfigureAwait ) ، لا يمكنك فعل أي شيء يفترض أنك في السياق الحالي (على سبيل المثال ، عناصر تحديث واجهة المستخدم).

لمزيد من المعلومات ، راجع مقال MSDN Best Practices in Asynchronous Programming .

2) استخدام Task.Run استدعاء أساليب منضم CPU.

يجب عليك استخدام Task.Run ، ولكن ليس ضمن أي رمز تريد أن تكون قابلة لإعادة الاستخدام (أي رمز المكتبة). لذلك تستخدم Task.Run استدعاء الأسلوب ، وليس كجزء من تنفيذ الأسلوب.

لذا ، يبدو العمل المعتمد على وحدة المعالجة المركزية على النحو التالي:

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

التي يمكنك الاتصال بها باستخدام Task.Run :

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

يجب أن يكون لديك أساليب التي تكون خليط من CPU-bound و I / O-bound توقيع Async مع وثائق تشير إلى طبيعتها المرتبطة CPU:

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

والتي يمكنك أيضًا الاتصال بها باستخدام Task.Run (نظرًا لأنه جزء من وحدة المعالجة المركزية مرتبط):

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

أود أن أسألك عن رأيك حول البنية الصحيحة عند استخدام Task.Run . أواجه واجهة المستخدم laggy UI في تطبيق WPF. NET 4.5 (مع إطار Caliburn Micro).

أساسا أنا أفعل (مقتطفات رمز مبسطة جدا):

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 await واجهة المستخدم ، ولكن لا يزال قيد التشغيل على مؤشر ترابط واجهة المستخدم ، لذلك فهو يجعله متخلفًا.

أين هو أفضل مكان لوضع Task.Run؟

يجب علي فقط

  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
}

هذه طريقة للقيام بذلك. يمكن تحقيق Task المناسبة بدون تشغيل Task.Run ... على الطرف الأمامي من التعليمات البرمجية لأن ذلك يُهزم الهدف. إذا كانت Task غير async فيجب أن تكون قادراً على الاتصال في await TaskAsync() ... وإليك git repo الذي يحتوي على مثال رائع: AsyncAwaitTasksExample . يحتوي على جهاز توقيت مباشر على واجهة المستخدم وخيارات لمختلف Tasks وطرق استخدامها. الوحيد الذي يعمل يشبه أدناه.

Task<String> GetStringAsync() {
    return Task.Run<String>(() => {
         return "Hello World";
    });
}

//Run the task with Async await
void async SomeFunction() {
     var msg = await GetStringAsync();
}






async-await