İşte async çözümlerini kullanarak farkı ve çeşitli sorunları göstermek için son zamanlarda kullandığım kod parçacıkları dizisi.
GUI tabanlı uygulamanızda çok fazla zaman alan bir olay işleyiciniz olduğunu ve bu nedenle onu eşzamansız yapmak istediğinizi varsayalım. İşte başladığınız eşzamanlı mantık:
while (true) {
string result = LoadNextItem().Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
LoadNextItem, sonunda incelemek istediğiniz bir sonuç üretecek bir Görev döndürür. Mevcut sonuç aradığınız sonuçsa, kullanıcı arayüzündeki bazı sayacın değerini günceller ve yöntemden dönersiniz. Aksi takdirde, LoadNextItem'den daha fazla öğe işlemeye devam edersiniz.
Eşzamansız sürüm için ilk fikir: sadece devamları kullanın! Ve şimdilik döngü kısmını görmezden gelelim. Demek istediğim, ne ters gidebilir ki?
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
});
Harika, artık engellemeyen bir yöntemimiz var! Bunun yerine çöküyor. UI kontrollerinde yapılan herhangi bir güncelleme, UI iş parçacığında yapılmalıdır, bu yüzden bunu hesaba katmanız gerekecektir. Neyse ki, devam ettirmelerin nasıl planlanacağını belirleme seçeneği var ve sadece bunun için varsayılan bir tane var:
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
Harika, artık çökmeyen bir yöntemimiz var! Bunun yerine sessizce başarısız oluyor. Devam etmeler, statüleri önceki görevinkine bağlı olmayan ayrı görevlerdir. Dolayısıyla, LoadNextItem hatası olsa bile, arayan yalnızca başarıyla tamamlanmış bir görevi görür. Tamam, eğer varsa istisnayı iletin:
return LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
throw t.Exception.InnerException;
}
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
Harika, şimdi bu gerçekten çalışıyor. Tek bir öğe için. Şimdi, bu döngüye ne dersiniz? Orijinal eşzamanlı sürümün mantığına eşdeğer bir çözüm şöyle görünecektir:
Task AsyncLoop() {
return AsyncLoopTask().ContinueWith(t =>
Counter.Value = t.Result,
TaskScheduler.FromCurrentSynchronizationContext());
}
Task<int> AsyncLoopTask() {
var tcs = new TaskCompletionSource<int>();
DoIteration(tcs);
return tcs.Task;
}
void DoIteration(TaskCompletionSource<int> tcs) {
LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
tcs.TrySetException(t.Exception.InnerException);
} else if (t.Result.Contains("target")) {
tcs.TrySetResult(t.Result.Length);
} else {
DoIteration(tcs);
}});
}
Veya yukarıdakilerin hepsi yerine, aynı şeyi yapmak için eşzamansız kullanabilirsiniz:
async Task AsyncLoop() {
while (true) {
string result = await LoadNextItem();
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
}
Bu şimdi çok daha güzel, değil mi?
Wait
ikinci örnekte çağrıyı daha sonra iki parçacıkları (çoğunlukla) eşdeğer olacaktır.