Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Görevi zaman uyumsuz programlama (TAP) modeli tipik zaman uyumsuz kodlamaya göre bir soyutlama katmanı sağlar. Bu modelde, kodu her zamanki gibi bir deyim dizisi olarak yazarsınız. Aradaki fark, derleyici her deyimi işlerken ve sonraki deyimi işlemeye başlamadan önce görev tabanlı kodunuzu okuyabilmenizdir. Bu modeli gerçekleştirmek için, derleyici her görevi tamamlamak için birçok dönüştürme gerçekleştirir. Bazı deyimler çalışmayı başlatabilir ve devam eden çalışmayı temsil eden bir Task nesnesi döndürebilir ve derleyicinin bu dönüştürmeleri çözümlemesi gerekir. Görev zaman uyumsuz programlamanın amacı, bir dizi ifade gibi okunan ancak daha karmaşık bir sırada yürütülen kodlar oluşturmayı sağlamaktır. Yürütme, dış kaynak ayırmayı ve görevlerin ne zaman tamamlanmasını temel alır.
Zaman uyumsuz görev programlama modeli, kişilerin zaman uyumsuz görevler içeren işlemler için yönergeler vermesine benzer. Bu makalede, async
ve await
anahtar sözcüklerinin bir dizi zaman uyumsuz yönerge içeren kod hakkında daha kolay mantık yürütmeyi nasıl kolaylaştırdığını göstermek için kahvaltı yapma yönergelerini içeren bir örnek kullanılmaktadır. Kahvaltı yapma yönergeleri liste olarak sağlanabilir:
- Bir fincan kahve koyun.
- Bir tava ısıtın, sonra iki yumurta kızartın.
- Üç dilim domuz pastırması kızartın.
- İki dilim ekmek kızartın.
- Tost üzerine tereyağı ve reçel sürün.
- Bir bardak portakal suyu dökün.
Yemek pişirme konusunda deneyimliyseniz, bu yönergeleri asenkron olaraktamamlayabilirsiniz. Tavayı yumurta için ısıtmaya başlarsınız, sonra domuz pastırmasını kızartmaya başlarsınız. Ekmeği tost makinesine koyarsın, sonra yumurtaları pişirmeye başlarsın. İşlemin her adımında bir görev başlatırsınız ve sonra dikkatinize hazır olan diğer görevlere geçersiniz.
Kahvaltı pişirmek, paralel olmayan zaman uyumsuz çalışmaların iyi bir örneğidir. Bir kişi (ya da iş parçacığı) tüm görevleri işleyebilir. Bir kişi, önceki görev tamamlanmadan bir sonraki görevi başlatarak zaman uyumsuz olarak kahvaltı yapabilir. Her pişirme görevi, birinin işlemi etkin olarak izleyip izlemediğine bakılmaksızın ilerler. Yumurtalar için tavayı ısıtmaya başlar başlamaz, domuz pastırmasını kızartmaya başlayabilirsiniz. Domuz pastırması pişirmeye başladıktan sonra ekmeği tost makinesine koyabilirsiniz.
Paralel algoritma için, yemek pişiren birden çok kişiye (veya birden çok iş parçacığına) ihtiyacınız vardır. Bir kişi yumurta pişirir, bir diğeri domuz pastırmasını kızartır ve bu şekilde devam eder. Her kişi kendi belirli bir görevine odaklanır. Yemek yapan her kişi (veya her bir iş parçacığı) geçerli görevin tamamlanmasını beklerken eş zamanlı olarak engellenir: Domuz pastırması çevirmek için hazır, ekmek kızartma makinesinde fırından çıkmak üzere vb.
30 dakikada tamamlanan yedi sıralı görev listesi olarak kahvaltı hazırlama yönergelerini gösteren
C# kod deyimleriyle yazılan zaman uyumlu yönergelerin listesini göz önünde bulundurun:
using System;
using System.Threading.Tasks;
namespace AsyncBreakfast
{
// These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }
class Program
{
static void Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Egg eggs = FryEggs(2);
Console.WriteLine("eggs are ready");
Bacon bacon = FryBacon(3);
Console.WriteLine("bacon is ready");
Toast toast = ToastBread(2);
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
}
private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast");
private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast");
private static Toast ToastBread(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
Task.Delay(3000).Wait();
Console.WriteLine("Remove toast from toaster");
return new Toast();
}
private static Bacon FryBacon(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
Task.Delay(3000).Wait();
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
Task.Delay(3000).Wait();
Console.WriteLine("Put bacon on plate");
return new Bacon();
}
private static Egg FryEggs(int howMany)
{
Console.WriteLine("Warming the egg pan...");
Task.Delay(3000).Wait();
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
Task.Delay(3000).Wait();
Console.WriteLine("Put eggs on plate");
return new Egg();
}
private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
}
}
}
Bu yönergeleri bir bilgisayar gibi yorumlarsanız kahvaltının hazırlanması yaklaşık 30 dakika sürer. Süre, tek tek görev sürelerinin toplamıdır. Bilgisayar, tüm çalışmalar tamamlanana kadar her deyimi engeller ve sonraki görev deyimine devam eder. Bu yaklaşım önemli zaman alabilir. Kahvaltı örneğinde bilgisayar yöntemi tatmin edici olmayan bir kahvaltı oluşturur. Eşzamanlı listede yer alan, ekmek kızartmak gibi sonraki görevler, önceki görevler tamamlanana kadar başlamaz. Kahvaltı servise hazır olmadan önce bazı yiyecekler soğur.
Bilgisayarın yönergeleri zaman uyumsuz olarak yürütmesini istiyorsanız, zaman uyumsuz kod yazmanız gerekir. İstemci programları yazarken, kullanıcı arabiriminin kullanıcı girişine yanıt vermesini istersiniz. Uygulamanız web'den veri indirirken tüm etkileşimi dondurmamalıdır. Sunucu programları yazarken, diğer isteklere hizmet ediyor olabilecek iş parçacıklarını engellemek istemezsiniz. Zaman uyumsuz alternatifler mevcut olduğunda zaman uyumlu kod kullanmak, ölçeği daha az maliyetli bir şekilde genişletmenize zarar verir. Engellenen iş parçacıkları için ödeme yapabilirsiniz.
Başarılı modern uygulamalar zaman uyumsuz kod gerektirir. Dil desteği olmadan, zaman uyumsuz kod yazmak için geri çağırmalar, tamamlama olayları veya kodun özgün amacını gizleyen başka bir araç gerekir. Zaman uyumlu kodun avantajı, taramayı ve anlamayı kolaylaştıran adım adım eylemdir. Geleneksel zaman uyumsuz modeller, kodun temel eylemlerine değil, kodun zaman uyumsuz doğasına odaklanmanızı zorlar.
Engellemeyin, onun yerine bekleyin
Önceki kod, talihsiz bir programlama uygulamasını vurgular: Asenkron işlemler gerçekleştirmek için senkron kod yazma. Kod, mevcut iş parçacığının başka bir iş yapmasını engeller. Çalışan görevler varken kod iş parçacığını kesintiye uğratmaz. Bu modelin sonucu, ekmeği koyduktan sonra tost makinesine bakmaya benzer. Kesintileri yoksayarsınız ve ekmek açılana kadar diğer görevleri başlatmazsınız. Tereyağını ve reçeli buzdolabından çıkaramazsın. Ocakta başlayan bir yangını görmeyi kaçırabilirsiniz. Hem ekmeği kızartmak hem de diğer işleri/sorunları aynı anda halletmek istiyorsunuz. Aynı durum kodunuz için de geçerlidir.
İş parçacığının görevler yürütülürken engellenmemesi için kodu güncelleyerek işe başlayabilirsiniz.
await
anahtar sözcüğü, bir görevi başlatmak ve görev tamamlandığında yürütmeye devam etmek için engelleyici olmayan bir yol sağlar. Kahvaltı kodunun basit bir zaman uyumsuz sürümü aşağıdaki kod parçacığına benzer:
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Egg eggs = await FryEggsAsync(2);
Console.WriteLine("eggs are ready");
Bacon bacon = await FryBaconAsync(3);
Console.WriteLine("bacon is ready");
Toast toast = await ToastBreadAsync(2);
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
Kod, FryEggs
, FryBacon
ve ToastBread
özgün yöntem gövdelerini sırasıyla Task<Egg>
, Task<Bacon>
ve Task<Toast>
nesnelerini döndürecek şekilde günceller. Güncellenmiş yöntem adları "Async" son ekini içerir: FryEggsAsync
, FryBaconAsync
, ve ToastBreadAsync
.
Main
yöntemi, tasarım gereği Task
bir ifadesi olmasa da return
nesnesini döndürür. Daha fazla bilgi için bkz. void döndüren zaman uyumsuz bir işlevin değerlendirilmesi.
Not
Güncelleştirilmiş kod henüz zaman uyumsuz programlamanın temel özelliklerinden yararlanmaz ve bu da daha kısa tamamlanma sürelerine neden olabilir. Kod, görevleri kabaca ilk zaman uyumlu sürümle aynı süre içinde işler. Tam yöntem uygulamaları için bu makalenin devamında kodun son sürümüne bakın.
Güncelleştirilmiş koda kahvaltı örneğini uygulayalım. Yumurtalar veya domuz pastırması pişirilirken iş parçacığı engellenmez, ancak mevcut çalışma tamamlanana kadar kod başka görevleri de başlatmaz. Ekmekleri hâlâ ekmek kızartma makinesine koyar ve ekmekler fırlayana kadar ekmek kızartma makinesine bakarsınız, ancak artık kesintilere yanıt verebilirsiniz. Birden çok siparişin verildiği bir restoranda, başka bir yemek pişirirken aşçı yeni bir sipariş başlatabilir.
Güncellenmiş kodda, kahvaltı hazırlığında çalışan iş parçacığı, başlatılan herhangi bir tamamlanmamış görev beklenirken engellenmez. Bazı uygulamalarda ihtiyacınız olan tek şey bu değişikliktir. Web'den veri indirilirken uygulamanızın kullanıcı etkileşimini desteklemesini sağlayabilirsiniz. Diğer senaryolarda, önceki görevin tamamlanmasını beklerken diğer görevleri başlatmak isteyebilirsiniz.
Görevleri eşzamanlı olarak başlatma
Çoğu işlem için birkaç bağımsız görevi hemen başlatmak istiyorsunuz. Her görev tamamlandıktan sonra başlamaya hazır olan diğer işleri başlatırsınız. Bu metodolojiyi kahvaltı örneğine uyguladığınızda kahvaltıyı daha hızlı hazırlayabilirsiniz. Ayrıca her şeyi aynı zamana yakın hazırlarsınız, böylece sıcak kahvaltının tadını çıkarabilirsiniz.
System.Threading.Tasks.Task sınıfı ve ilgili türler, devam eden görevlere bu mantık stilini uygulamak için kullanabileceğiniz sınıflardır. Bu yaklaşım, gerçek hayatta kahvaltı oluşturma şeklinize daha yakın bir kod yazmanızı sağlar. Yumurta, domuz pastırması ve tostu aynı anda pişirmeye başlarsınız. Her yiyecek öğesi eylem gerektirdiğinden, dikkatinizi bu göreve çevirir, eylemle ilgilenir ve sonra dikkatinizi gerektiren başka bir şey beklersiniz.
Kodunuzda bir görev başlatır ve çalışmayı temsil eden Task nesnesini tutarsınız. Sonuç hazır olana kadar işi ertelemek için görevdeki await
yöntemini kullanırsınız.
Bu değişiklikleri kahvaltı koduna uygulayın. İlk adım, await
ifadesini kullanmak yerine, işlemler başladığında görevleri depolamaktır:
Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");
Task<Egg> eggsTask = FryEggsAsync(2);
Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");
Task<Bacon> baconTask = FryBaconAsync(3);
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");
Task<Toast> toastTask = ToastBreadAsync(2);
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");
Console.WriteLine("Breakfast is ready!");
Bu düzeltmeler kahvaltınızı daha hızlı hazırlamanıza yardımcı olmaz.
await
ifadesi, başlar başlamaz tüm görevlere uygulanır. Bir sonraki adım, kahvaltıyı sunmadan önce domuz pastırması ve yumurtalar için await
ifadelerini yöntemin sonuna taşımaktır:
Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");
Task<Egg> eggsTask = FryEggsAsync(2);
Task<Bacon> baconTask = FryBaconAsync(3);
Task<Toast> toastTask = ToastBreadAsync(2);
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");
Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");
Console.WriteLine("Breakfast is ready!");
Artık tüm süreyi adamak zorunda kalmadan yaklaşık 20 dakikada hazırlayabileceğiniz bir kahvaltınız var. Bazı görevler aynı anda çalıştığından toplam pişirme süresi azalır.
Kod güncelleştirmeleri pişirme süresini azaltarak hazırlık sürecini geliştirir, ancak yumurta ve domuz pastırması yakarak bir regresyon oluşturur. Tüm zaman uyumsuz görevleri aynı anda başlatırsınız. Her görevde yalnızca sonuçlara ihtiyacınız olduğunda beklersiniz. Kod, farklı mikro hizmetlere istekte bulunan ve ardından sonuçları tek bir sayfada birleştiren bir web uygulamasındaki programa benzer olabilir. Tüm istekleri hemen yapar ve ardından tüm bu görevlere await
ifadesini uygular ve web sayfasını oluşturursunuz.
Görevlerle destek yapılandırma
Önceki kod düzeltmeleri, tost dışında kahvaltı için her şeyi aynı anda hazırlamaya yardımcı olur. Tost yapma süreci, asenkron bir işlem (ekmeği kızartmak) ile senkron işlemler (tostun üzerine tereyağı ve reçel sürmek) bir bileşimdir. Bu örnekte, zaman uyumsuz programlama hakkında önemli bir kavram gösterilmektedir:
Önemli
Zaman uyumsuz bir işlemin ve ardından zaman uyumlu çalışmanın bileşimi zaman uyumsuz bir işlemdir. Başka bir şekilde ifade etmek gerekirse, bir işlemin herhangi bir bölümü zaman uyumsuzsa, işlemin tamamı zaman uyumsuzdur.
Önceki güncelleştirmelerde, çalışan görevleri tutmak için Task veya Task<TResult> nesneleri kullanmayı öğrendinsiniz. Her görevin sonucunu kullanmadan önce beklemeniz gerekir. Sonraki adım, diğer çalışmaların birleşimini temsil eden yöntemler oluşturmaktır. Kahvaltı sunmadan önce, tereyağı ve reçeli yaymadan önce ekmeğin kızartılmasını temsil eden görevi beklemek istiyorsunuz.
Bu çalışmayı aşağıdaki kodla temsil edebilirsiniz:
static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast);
return toast;
}
MakeToastWithButterAndJamAsync
yöntemi, derleyiciye yöntemin bir async
ifadesi içerdiğini ve zaman uyumsuz işlemler içerdiğini belirten await
değiştiricisine sahiptir. Metod, önce ekmeği kızartır, sonra tereyağını ve reçeli yayar görevi temsil eder. yöntemi, üç işlemin bileşimini temsil eden bir Task<TResult> nesnesi döndürür.
Düzeltilen ana kod bloğu şu şekilde görünür:
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
var eggsTask = FryEggsAsync(2);
var baconTask = FryBaconAsync(3);
var toastTask = MakeToastWithButterAndJamAsync(2);
var eggs = await eggsTask;
Console.WriteLine("eggs are ready");
var bacon = await baconTask;
Console.WriteLine("bacon is ready");
var toast = await toastTask;
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
Bu kod değişikliği, zaman uyumsuz kodla çalışmaya yönelik önemli bir tekniği gösterir. İşlemleri bir görev döndüren yeni bir yönteme ayırarak görevleri oluşturursunuz. Bu görevde ne zaman bekleyebileceğinizi seçebilirsiniz. Diğer görevleri eşzamanlı olarak başlatabilirsiniz.
Zaman uyumsuz özel durumları işleme
Bu noktaya kadar kodunuz tüm görevlerin başarıyla tamamlanmasını örtük olarak varsayar. Zaman uyumsuz yöntemler, zaman uyumlu karşılıkları gibi istisnalar fırlatır. Özel durumlar ve hata işleme için zaman uyumsuz desteğin hedefleri genel olarak zaman uyumsuz destekle aynıdır. En iyi uygulama, senkron ifadeler dizisi gibi okunan kod yazmaktır. Görevler başarıyla tamamlanamadıklarında istisnalar oluşturur. İstemci kodu, await
ifadesi başlatılan göreve uygulandığında bu özel durumları yakalayabilir.
Kahvaltı örneğinde, ekmek kızartırken tost makinesinin alev aldığını varsayalım.
ToastBreadAsync
yöntemini aşağıdaki kodla eşleşecek şekilde değiştirerek bu sorunun benzetimini yapabilirsiniz:
private static async Task<Toast> ToastBreadAsync(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
await Task.Delay(2000);
Console.WriteLine("Fire! Toast is ruined!");
throw new InvalidOperationException("The toaster is on fire");
await Task.Delay(1000);
Console.WriteLine("Remove toast from toaster");
return new Toast();
}
Not
Bu kodu derlerken ulaşılamayan kodla ilgili bir uyarı görürsünüz. Bu hata tasarım gereğidir. Tost makinesi yandıktan sonra işlemler normal şekilde devam etmediğinden kod bir hata döndürür.
Kod değişikliklerini yaptıktan sonra uygulamayı çalıştırın ve çıkışı denetleyin:
Pouring coffee
Coffee is ready
Warming the egg pan...
putting 3 slices of bacon in the pan
Cooking first side of bacon...
Putting a slice of bread in the toaster
Putting a slice of bread in the toaster
Start toasting...
Fire! Toast is ruined!
Flipping a slice of bacon
Flipping a slice of bacon
Flipping a slice of bacon
Cooking the second side of bacon...
Cracking 2 eggs
Cooking the eggs ...
Put bacon on plate
Put eggs on plate
Eggs are ready
Bacon is ready
Unhandled exception. System.InvalidOperationException: The toaster is on fire
at AsyncBreakfast.Program.ToastBreadAsync(Int32 slices) in Program.cs:line 65
at AsyncBreakfast.Program.MakeToastWithButterAndJamAsync(Int32 number) in Program.cs:line 36
at AsyncBreakfast.Program.Main(String[] args) in Program.cs:line 24
at AsyncBreakfast.Program.<Main>(String[] args)
Tost makinesinin alev aldığı ve sistemin özel durumu gözlemlediği zaman arasında oldukça az sayıda görev tamamlandığını unutmayın. Zaman uyumsuz olarak çalıştırılan bir görev özel durum oluşturursa, bu görev hatalı.
Task
nesnesi, Task.Exception özelliğinde oluşan özel durumu tutar. Hatalı görevler, göreve await
ifadesi uygulandığında bir istisna fırlatır.
Bu işlem hakkında anlaşılması gereken iki önemli mekanizma vardır:
- Hatalı bir görevin içerisinde bir özel durum nasıl saklanır?
- Kod hatalı bir görevde (
await
) beklerken özel durumun nasıl paketten çıkarıldığı ve yeniden dağıtıldığı
Zaman uyumsuz olarak çalıştırılan kod bir özel durum oluşturursa, özel durum Task
nesnesinde depolanır. Eşzamansız çalışma sırasında birden fazla istisna fırlatılabileceğinden Task.Exception özelliği System.AggregateException bir nesnedir. Oluşan özel durumlar AggregateException.InnerExceptions koleksiyonuna eklenir.
Exception
özelliği null ise, yeni bir AggregateException
nesnesi oluşturulur ve oluşturulan özel durum koleksiyondaki ilk öğedir.
Hatalı bir görevin en yaygın senaryosu, Exception
özelliğinin tam olarak bir özel durum içermesidir. Kodunuz hata veren bir görevde beklediğinde, koleksiyondaki ilk AggregateException.InnerExceptions özel durumunu yeniden fırlatır. Bu sonuç, örnekteki çıktının System.InvalidOperationException nesnesi yerine AggregateException
nesnesini göstermesinin nedenidir. İlk iç özel durumun ayıklanması, zaman uyumsuz yöntemlerle çalışmayı, zaman uyumlu karşılıklarıyla çalışmaya mümkün olduğunca benzer hale getirir. Senaryonuz birden çok özel durum oluşturabileceğinden kodunuzda Exception
özelliğini inceleyebilirsiniz.
Bahşiş
Önerilen uygulama, görev döndüren yöntemlerden zaman uyumlu bağımsız değişken doğrulama özel durumlarının ortaya çıkmasıdır. Daha fazla bilgi ve örnek için bkz. görev döndüren yöntemlerdeki özel durumlar.
Sonraki bölüme geçmeden önce, ToastBreadAsync
yönteminizdeki aşağıdaki iki deyimi yorum satırı haline getirin. Bir yangın daha başlatmak istemezsiniz:
Console.WriteLine("Fire! Toast is ruined!");
throw new InvalidOperationException("The toaster is on fire");
Await ifadelerini görevlere verimli bir şekilde uygulayın
await
sınıfının yöntemlerini kullanarak önceki kodun sonundaki Task
ifade serisini geliştirebilirsiniz. API'lerden biri, bağımsız değişken listesindeki tüm görevler tamamlandığında tamamlanan bir WhenAll nesnesi döndüren Task yöntemidir. Aşağıdaki kod bu yöntemi gösterir:
await Task.WhenAll(eggsTask, baconTask, toastTask);
Console.WriteLine("Eggs are ready");
Console.WriteLine("Bacon is ready");
Console.WriteLine("Toast is ready");
Console.WriteLine("Breakfast is ready!");
Diğer bir seçenek, bağımsız değişkenlerinden herhangi biri tamamlandığında tamamlanan bir WhenAny nesnesi döndüren Task<Task>
yöntemini kullanmaktır. Görevin tamamlanmasını bildiğiniz için döndürülen görevde bekleyebilirsiniz. Aşağıdaki kodda, ilk görevin tamamlanmasını beklemek ve ardından sonucunu işlemek için WhenAny yöntemini nasıl kullanabileceğiniz gösterilmektedir. Tamamlanan görevden sonucu işledikten sonra, tamamlanmış görevi WhenAny
yöntemine geçirilen görevler listesinden kaldırırsınız.
var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("Eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("Bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("Toast is ready");
}
await finishedTask;
breakfastTasks.Remove(finishedTask);
}
Kod parçacığının sonuna yakın bir await finishedTask;
ifadesine dikkat edin.
await Task.WhenAny
ifadesi tamamlanmış görevi beklemez, bunun yerine Task
yöntemi tarafından döndürülen Task.WhenAny
nesnesinde bekler.
Task.WhenAny
yönteminin sonucu tamamlanmış (veya hatalı) görevdir. En iyi yöntem, görevin tamam olduğunu bildiğinizde bile görevi yeniden beklemektir. Bu şekilde, görev sonucunu alabilir veya görevin bir hatayla sonuçlanmasına neden olan herhangi bir özel durumun ortaya çıkmasını sağlayabilirsiniz.
Son kodu gözden geçirme
Kodun son sürümü şöyle görünür:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace AsyncBreakfast
{
// These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }
class Program
{
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
var eggsTask = FryEggsAsync(2);
var baconTask = FryBaconAsync(3);
var toastTask = MakeToastWithButterAndJamAsync(2);
var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("toast is ready");
}
await finishedTask;
breakfastTasks.Remove(finishedTask);
}
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast);
return toast;
}
private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
}
private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast");
private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast");
private static async Task<Toast> ToastBreadAsync(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
await Task.Delay(3000);
Console.WriteLine("Remove toast from toaster");
return new Toast();
}
private static async Task<Bacon> FryBaconAsync(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
await Task.Delay(3000);
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
await Task.Delay(3000);
Console.WriteLine("Put bacon on plate");
return new Bacon();
}
private static async Task<Egg> FryEggsAsync(int howMany)
{
Console.WriteLine("Warming the egg pan...");
await Task.Delay(3000);
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
await Task.Delay(3000);
Console.WriteLine("Put eggs on plate");
return new Egg();
}
private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
}
}
}
Kod, zaman uyumsuz kahvaltı görevlerini yaklaşık 15 dakika içinde tamamlar. Bazı görevler aynı anda çalıştırıldığından toplam süre azalır. Kod aynı anda birden çok görevi izler ve yalnızca gerektiğinde eylemde bulunur.
Son kod zaman uyumsuzdur. Bir kişinin kahvaltıyı nasıl pişirebileceğini daha doğru bir şekilde yansıtır. Son kodu makaledeki ilk kod örneğiyle karşılaştırın. Temel eylemler, kodu okuyarak hala açıktır. Son kodu, makalenin başında gösterildiği gibi kahvaltı yapma yönergelerini okuduğunuz gibi okuyabilirsiniz.
async
ve await
anahtar sözcüklerinin dil özellikleri, her kişinin yazılı yönergeleri izlemek için yaptığı çeviriyi sağlar: Görevleri olabildiğince başlatın ve görevlerin tamamlanmasını beklerken engellemeyin.
Sonraki adım
Zaman uyumsuz programlar için gerçek dünya senaryolarını keşfedin