8

this excellent one here dahil olmak üzere çok sayıda kitap ve bloglara dayanarak, kişinin yardımcı async yöntemlerini, yani sarmalayıcı yöntemlerini açığa çıkaran bir dll kütüphanesi yazdığında, bunun genellikle dahili olarak en iyi uygulama olduğu düşünülür. bir threadpool iplik gerçek zaman uyumsuz yöntem I/o görevi tamamlamak gibi pek (sözde kod kısalık için aşağıda gösterilen ve bir örnek olarak HttpClient kullanıyorum)C# ConfigureAwait (false) ile asenkronizasyon/zincir oluşturma

public Async Task<HttpResponseMessage> MyMethodAsync(..) 
{ 
    ... 
    var httpClient = new HttpClient(..); 
    var response = await httpClient.PostAsJsonAsync(..).ConfigureAwait(false); 
    ... 
    return response; 
} 

burada anahtar, böylece ConfigureAwait(false) kullanımıdır IO görev tamamlama, orijinal iş parçacığı bağlamı yerine bir iş parçacığı iş parçacığı üzerinde gerçekleşir, dolayısıyla kilitlenme olasılığını önler.

Sorumu arayan kişinin bakış açısından. Aşağıdaki örnekte görüldüğü gibi, arayan ve yukarıdaki yöntem çağrısı arasında yöntem çağrıları katmanlarının olduğu bir senaryo ile özellikle ilgileniyorum.

CallerA -> Method1Async -> Method2Async -> finally the above MyMethodAsync 

yeterince sadece nihai yöntemi ConfigureAwait(false) olması veya bir zamanda Method1Async sağlamalıdır ve Method2Async ayrıca yurt ConfigureAwait(false) ile zaman uyumsuz yöntemleri çağırmak bunu midir? Tüm bu aracı yöntemlere dahil edilmesi, özellikle de Method1Async ve Method2Async'un MyMethodAsync numaralı çağrıya son veren aşırı yüklenmeler olması saçma gibi görünüyor. Herhangi bir düşünce, lütfen bizi aydınlatın! Aşağıdaki özel zaman uyumsuz yöntemi ile bir kütüphane var Yani eğer

private async Task<string> MyPrivateMethodAsync(MyClass myClass) 
{ 
    ... 
    return await SomeObject.ReadAsStringAsync().ConfigureAwait(false); 
} 

Ben aşağıda gösterildiği gibi aşağıdaki kamu aşırı yöntemler hem de (yanlış) ConfigureAwait de emin olmalısınız, Örnek ile güncellendi?

public async Task<string> MyMethodAsync(string from) 
{ 
     return await MyPrivateMethodAsync(new (MyClass() { From = from, To = "someDefaultValue"}).ConfigureAwait(false); 
} 
public async Task<string> MyMethodAsync(string from, string to) 
{ 
     return await MyPrivateMethodAsync(new (MyClass() { From = from, To = to }).ConfigureAwait(false); 
} 
+0

'ConfigureAwait (false)' kütüphane kodu için iyi bir uygulama olsa da, kullanımının kendi anlamları olabilir: http://stackoverflow.com/q/28410046 – Noseratio

cevap

9

Kesinlikle değil. Adının önerdiği gibi ConfigureAwait, await'u yapılandırır. Sadece onunla birlikte await etkiler.

ConfigureAwait aslında farklı bir awaitable türü, ConfiguredTaskAwaitable yerine Task tüm await s için SynchronizationContext göz ardı istiyorsanız sırayla TaskAwaiter

yerine farklı bir awaiter türü ConfiguredTaskAwaiter döndürür her biri için ConfigureAwait(false) kullanmalıdır döndürür onların Eğer kullanabilirsiniz ConfigureAwait(false) kullanımını sınırlamak isterseniz

benim NoSynchronizationContextScope en üste (here bakınız):

görev beklenmektedir
async Task CallerA() 
{ 
    using (NoSynchronizationContextScope.Enter()) 
    { 
     await Method1Async(); 
    } 
} 
+1

Bence her biri için * gerekir ... * "biraz fazla sıkı" - bir satırda 2 + bekliyor "bekliyor" ve ilk kez gerçekten zaman uyumsuz geri döndüğünüzde geri dönmek zorunda değilsiniz 'ConfigureAwait (false)' geri döndüğünde bağlam kaybettiniz ... Ama gerçekte 'ConfigureAwait (false)' tüm çağrılar (veya hiçbiri) olmalıdır. –

+1

@ i3arnon, harika görünen ve kullanım durumunu anlamak istediğim NoSynchronizationContextScope'unuz hakkında hızlı bir soru. Yukarıdaki örneğimle devam edersek, NoSynchronizationContextScope'unuzu eklemek için küçük bir fazladan kodlama ile, CallerA artık çağrı ağacındaki yöntemleri Method1Async -> Method2Async -> MyMethodAsync'e gönderir. veya bu yöntemlerin içinde ConfigureAwait (false) var mı? – SamDevx

+1

@SamDevx oldukça fazla. Aslında bundan daha sert. Geçici olarak SC'yi ortadan kaldırır, böylece iç yöntemlerin yok sayılması için bir SC'si yoktur. – i3arnon

4

, bu bir TaskAwaiter tekabül tutmak için parçayı oluşturur Mevcut SynchronizationContext'u da yakalayan görev. Görev tamamlandıktan sonra, bekletici, yakalanan bağlamdaki beklemeye (devam) denir.

Yakalanan bağlama devamını çalışmaz bir awiatable farklı türde (ConfiguredTaskAwaitable) ve ilgili awaiter (ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) oluşturur ConfigureAwait(false), çağırarak bu önleyebilir.

Buradaki nokta, her bir await için, farklı bir bekçinin oluşturulduğu durumdur, yöntem veya programdaki tüm anwaitables'lar arasında paylaşılan bir şey değildir. Dolayısıyla, her bir await bildirimi için ConfigureAwait(false) numaralı telefonu aramanız en iyisidir.

Bekleyenler için kaynak kodunu here görebilirsiniz.

+0

Küçük bir düzeltme: "Görev" oluşturulduğunda, geçerli değil "SynchronizationContext" işlevini * yakalamaz. Task.GetAwaiter() 'çağrıldığında ('beklemek için derleyici tarafından oluşturulan kod'), 'SynchronizationContext' bu noktada yakalanır. – Noseratio

+0

@Noseratio Yorumunuz için teşekkürler. Bunu [Görev kaynağından] aldım (http://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/TaskAwaiter.cs,16e40fc537484d93). Task.GetAwaiter() 'bunu yaptığını düşünmese de yapmıyor. Ben –

+0

bir şey eksik olmalı Ben TaskAwaiter 'yaratıldığında SC yakalar demedim. "ICriticalNotifyCompletion.OnCompleted/UnsafeOnCompleted" [burada] (http://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/TaskAwaiter.cs,45f10a20f8fdfd61,references) uygulamasında bu anahtardır. herhangi bir bekçinin yöntemi. – Noseratio

İlgili konular