c# ما هي الاختلافات بين استخدام ConfigureAwait(false) و Task.Run؟




.net .net-4.5 (4)

أعي أنه من المستحسن استخدام ConfigureAwait(false) في await s في رمز المكتبة بحيث لا يتم تشغيل التعليمة البرمجية التالية في سياق تنفيذ المتصل ، والذي قد يكون مؤشر ترابط واجهة المستخدم. أنا أفهم أيضا أن await Task.Run(CpuBoundWork) ينبغي أن تستخدم بدلا من CpuBoundWork() لنفس السبب.

مثال مع ConfigureAwait

public async Task<HtmlDocument> LoadPage(Uri address)
{
    using (var client = new HttpClient())
    using (var httpResponse = await client.GetAsync(address).ConfigureAwait(false))
    using (var responseContent = httpResponse.Content)
    using (var contentStream = await responseContent.ReadAsStreamAsync().ConfigureAwait(false))
        return LoadHtmlDocument(contentStream); //CPU-bound
}

مثال مع Task.Run

public async Task<HtmlDocument> LoadPage(Uri address)
{
    using (var client = new HttpClient())
    using (var httpResponse = await client.GetAsync(address))
        return await Task.Run(async () =>
        {
            using (var responseContent = httpResponse.Content)
            using (var contentStream = await responseContent.ReadAsStreamAsync())
                return LoadHtmlDocument(contentStream); //CPU-bound
        });
}

ما هي الاختلافات بين هذين النهجين؟


تمت الموافقة على @ Stephen بالإجابة ، إذا استمر التشويش راجع أدناه لقطات الشاشة 1 # بدون ConfigureAwait (خطأ)
انظر أدناه صورة الموضوع الرئيسي محاولة تحديث التسمية

2 # مع ConfigureAwait (خطأ)
انظر أدناه موضوع العمل صورة تحاول تحديث التسمية


عندما تقول Task.Run ، فأنت تقول أن لديك بعض عمل وحدة المعالجة المركزية للقيام بذلك قد يستغرق وقتًا طويلاً ، لذلك يجب أن يتم تشغيله دائمًا على مؤشر ترابط تجمّع مؤشر الترابط.

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


كملاحظة جانبية ، في كلتا الحالتين ، لا يزال بإمكان LoadPage() حظر مؤشر ترابط واجهة المستخدم الخاصة بك ، لأنك await client.GetAsync(address) يحتاج إلى وقت لإنشاء مهمة لتمريرها إلى ConfigureAwait(false) . وقد تكون العملية التي تستغرق وقتًا طويلاً قد بدأت بالفعل قبل إرجاع المهمة.

أحد الحلول الممكنة هو استخدام SynchronizationContextRemover من here :

public async Task<HtmlDocument> LoadPage(Uri address)
{
    await new SynchronizationContextRemover();

    using (var client = new HttpClient())
    using (var httpResponse = await client.GetAsync(address))
    using (var responseContent = httpResponse.Content)
    using (var contentStream = await responseContent.ReadAsStreamAsync())
        return LoadHtmlDocument(contentStream); //CPU-bound
}

في هذه الحالة ، سيكون الإصدار Task.Run الخاص بك أكثر قليلاً تحميل ، كما سيتم استدعاء الاستدعاء الأول (في await client.GetAsync(address) ) مرة أخرى في سياق الاستدعاء ، وكذلك نتائج استدعاء Task.Run .

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





c#-5.0