سؤال

لا أرى اختلافًا بين ميزات C#(و VB) الجديدة ، و .NET 4.0's المكتبة الموازية للمهمة. خذ ، على سبيل المثال ، رمز إريك ليبرت من هنا:

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

يبدو أن await الكلمة الرئيسية تخدم غرضين مختلفين. الحدوث الأول (FetchAsync) يبدو أنه يعني ، "إذا تم استخدام هذه القيمة في وقت لاحق في هذه الطريقة ولم تنته مهمتها ، انتظر حتى تكمل قبل المتابعة ". المثيل الثاني (archive) يبدو أنه يعني ، "إذا لم تنتهي هذه المهمة بعد ، انتظر فى الحال حتى يكمل ". إذا كنت مخطئا ، من فضلك تصحيح لي.

لا يمكن أن تكتب بسهولة مثل هذا؟

void ArchiveDocuments(List<Url> urls) {
    for(int i = 0; i < urls.Count; ++i) {
        var document = FetchAsync(urls[i]);       // removed await
        if (archive != null)
            archive.Wait();                       // changed to .Wait()
        archive = ArchiveAsync(document.Result);  // added .Result
    }
}

لقد استبدلت الأول await مع Task.Result حيث تكون القيمة مطلوبة بالفعل والثاني await مع Task.Wait(), ، حيث يحدث الانتظار بالفعل. الوظيفة هي (1) تم تنفيذه بالفعل ، و (2) أقرب بكثير إلى ما يحدث بالفعل في الكود.

أنا أدرك أن async تتم إعادة كتابة الطريقة كآلة حالة ، على غرار المتكررين ، لكنني أيضًا لا أرى الفوائد التي تجلبها. لا يزال أي رمز يتطلب تشغيل مؤشر ترابط آخر (مثل التنزيل) يتطلب مؤشر ترابط آخر ، ولا يزال بإمكان أي رمز (مثل القراءة من ملف) استخدام TPL للعمل مع مؤشر ترابط واحد فقط.

من الواضح أنني أفتقد شيئًا ضخمًا هنا ؛ هل يمكن لأي شخص مساعدتي في فهم هذا بشكل أفضل قليلاً؟

هل كانت مفيدة؟

المحلول

أعتقد أن سوء الفهم ينشأ هنا:

يبدو أن الكلمة الرئيسية في الانتظار تخدم غرضين مختلفين. يبدو أن الحدوث الأول (fetchasync) يعني ، "إذا تم استخدام هذه القيمة لاحقًا في هذه الطريقة ولم تنته مهمتها ، انتظر حتى تكمل قبل المتابعة." يبدو أن المثيل الثاني (الأرشيف) يعني ، "إذا لم تنتهي هذه المهمة بعد ، فانتظر الآن حتى تكمل". إذا كنت مخطئا ، من فضلك تصحيح لي.

هذا في الواقع غير صحيح تماما. كلاهما لهما نفس المعنى.

في حالتك الأولى:

var document = await FetchAsync(urls[i]);

ما يحدث هنا ، هو أن وقت التشغيل يقول "ابدأ في استدعاء FetchAsync ، ثم إرجاع نقطة التنفيذ الحالية إلى مؤشر الترابط الذي يدعو هذه الطريقة." لا يوجد "انتظار" هنا - بدلاً من ذلك ، يعود التنفيذ إلى سياق التزامن الدعوة ، وتستمر الأمور. في مرحلة ما من المستقبل ، ستتم إكمال مهمة FetchAsync ، وفي هذه المرحلة ، سيستأنف هذا الرمز في سياق مزامنة مؤشر ترابط الاتصال ، وسيحدث العبارة التالية (تعيين متغير المستند).

سيستمر التنفيذ بعد ذلك حتى يتم انتظار المكالمة الثانية - في ذلك الوقت ، سيحدث نفس الشيء - إذا كان Task<T> (الأرشيف) غير مكتمل ، وسيتم إصدار التنفيذ في سياق الاتصال - وإلا ، سيتم تعيين الأرشيف.

في الحالة الثانية ، تختلف الأمور تمامًا - هنا ، أنت تمنع صراحة ، مما يعني أن سياق التزامن الاستدعاء لن يحصل أبدًا على فرصة لتنفيذ أي رمز حتى يكمل طريقتك بأكملها. منحت ، لا يزال هناك عدم التزامن ، ولكن لا يوجد أي تزامن موجود بالكامل في كتلة الكود هذه - لن يحدث أي رمز خارج هذا الرمز الصافي في هذا الموضوع حتى يكمل جميع الكود الخاص بك.

نصائح أخرى

هناك فرق كبير:

Wait() الكتل ، await لا يمنع. إذا قمت بتشغيل نسخة Async من ArchiveDocuments() على موضوع واجهة المستخدم الرسومية ، سيبقى واجهة المستخدم الرسومية مستجيبة أثناء تشغيل عمليات الجلب والأرشفة. إذا كنت تستخدم إصدار TPL مع Wait(), ، سيتم حظر واجهة المستخدم الرسومية الخاصة بك.

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

قام أندرس بتثبيته إلى إجابة مختصرة للغاية في مقابلة القناة 9 المباشرة التي قام بها. انا اوصي بشده به

تتيح لك الكلمات الرئيسية الجديدة والكلمات الرئيسية في الانتظار تنظيم التزامن في تطبيقاتك. إنهم لا يقدمون بالفعل أي تزامن في طلبك.

TPL والمهمة بشكل أكثر تحديدا هي اتجاه واحد يمكنك استخدامها في الواقع في وقت واحد بشكل متزامن. تسمح لك Async و في انتظار الكلمة الرئيسية الجديدة مؤلف موسيقى هذه العمليات المتزامنة بطريقة "متزامنة" أو "خطي".

لذلك لا يزال بإمكانك كتابة تدفق خطي للتحكم في برامجك بينما قد يحدث أو لا يحدث الحوسبة الفعلية بشكل متزامن. عندما يحدث الحساب بشكل متزامن ، تسمح لك في الانتظار والمتزامن مؤلف موسيقى هذه العمليات.

القدرة على تحويل تدفق برنامج التحكم إلى آلة الحالة هو ما يجعل هذه الكلمات الرئيسية الجديدة في حالة تأثر. فكر في الأمر تحكم العائد, ، بدلا من القيم.

الدفع هذا الفيديو القناة 9 من أندرس يتحدث عن الميزة الجديدة.

المشكلة هنا هي أن توقيع ArchiveDocuments مضلل. لديها عودة صريحة void ولكن حقا العائد Task. بالنسبة لي ، يعني الفراغ متزامنًا لأنه لا توجد طريقة "للانتظار" حتى تنتهي. النظر في التوقيع البديل للوظيفة.

async Task ArchiveDocuments(List<Url> urls) { 
  ...
}

بالنسبة لي عندما يتم كتابته بهذه الطريقة يكون الفرق أكثر وضوحًا. ال ArchiveDocuments الوظيفة ليست وظيفة تكمل بشكل متزامن ولكنها ستنتهي لاحقًا.

الدعوة إلى FetchAsync() سيظل يحظر حتى يكمله (ما لم يكن هناك بيان ضمن المكالمات await؟) المفتاح هل يتم إرجاع هذا التحكم إلى المتصل (لأن ArchiveDocuments تم الإعلان عن الطريقة نفسها باسم async). لذلك يمكن للمتصل أن يستمر بسعادة في معالجة منطق واجهة المستخدم ، والرد على الأحداث ، إلخ.

متي FetchAsync() يكمل ، ويقاطع المتصل لإنهاء الحلقة. يضرب ArchiveAsync() والكتل ، ولكن ArchiveAsync() ربما ينشئ مهمة جديدة ، ويبدأها ، ويعيد المهمة. يتيح ذلك أن تبدأ الحلقة الثانية ، بينما يتم معالجة المهمة.

الحلقة الثانية يضرب FetchAsync() والكتل ، وعودة التحكم إلى المتصل. متي FetchAsync() يكمل ، ويقاطع مرة أخرى المتصل لمواصلة المعالجة. ثم يضرب await archive, ، والذي يعيد التحكم إلى المتصل حتى Task تم إنشاؤها في حلقة 1. بمجرد اكتمال هذه المهمة ، يتم مقاطعة المتصل مرة أخرى ، ومكالمات الحلقة الثانية ArchiveAsync(), ، والتي تحصل على مهمة بدء وتبدأ الحلقة 3 ، كرر AD غثيان.

المفتاح هو عودة التحكم إلى المتصل أثناء تنفيذ المصاعد الثقيل.

الكلمة الرئيسية في انتظار لا تقدم التزامن. يشبه الكلمة الرئيسية العائد ، ويطلب من المترجم إعادة هيكلة الكود الخاص بك في Lambda التي تتحكم فيها جهاز الحالة.

لمعرفة ما سيبدو عليه رمز في انتظار دون "انتظار" ، انظر هذا الرابط الممتاز: http://blogs.msdn.com/b/windowsappdev/archive/2012/04/24/diving-deep-with-winrt-and-await.aspx

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top