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.
Kodunuz ağ veri isteklerini, veritabanı erişimini veya dosya sistemi okuma/yazmalarını desteklemek için G/Ç'ye bağlı senaryolar uyguluyorsa, zaman uyumsuz programlama en iyi yaklaşımdır. Ayrıca, pahalı hesaplamalar gibi CPU'ya bağlı senaryolar için zaman uyumsuz kod da yazabilirsiniz.
C#, geri çağrılarla uğraşmak veya asenkronizmi destekleyen bir kütüphaneye uymak zorunda kalmadan kolayca asenkron kod yazmanızı sağlayan dil düzeyinde bir asenkron programlama modeline sahiptir. Model, Görev tabanlı zaman uyumsuz desen (TAP)olarak bilinen modeli izler.
Zaman uyumsuz programlama modelini keşfetme
Task ve Task<T> nesneleri zaman uyumsuz programlamanın çekirdeğini temsil eder. Bu nesneler, async ve await anahtar sözcükleri destekleyerek zaman uyumsuz işlemleri modellemek için kullanılır. Çoğu durumda model hem G/Ç hem de CPU'ya bağlı senaryolar için oldukça basittir. Bir async yönteminin içinde:
-
G/Ç ile ilişkili kod,
Taskyöntemindeki birTask<T>veyaasyncnesnesiyle temsil edilen bir işlemi başlatır. - CPU'ya bağlı kodTask.Run yöntemiyle bir arka plan iş parçacığı üzerinde bir işlem başlatır.
Her iki durumda da etkin bir Task, tamamlanmamış olabilir bir zaman uyumsuz işlemi temsil eder.
Anahtar await sözcük, sihrin gerçekleştiği yerdir.
await ifadesini içeren yöntemi çağırana denetim verir ve sonuçta kullanıcı arabiriminin yanıt vermesine veya bir hizmetin esnek olmasına izin verir.
olsa da, bu makale dil düzeyindeki yapılara odaklanır.
Not
Bu makalede sunulan bazı örnekler, web hizmetinden veri indirmek için System.Net.Http.HttpClient sınıfını kullanır. Örnek kodda, s_httpClient nesnesi Program sınıfı türünde statik bir alandır:
private static readonly HttpClient s_httpClient = new();
Daha fazla bilgi için bu makalenin sonundaki tam örnek kod bakın.
Temel kavramları gözden geçirme
C# kodunuzda zaman uyumsuz programlama uyguladığınızda, derleyici programınızı bir durum makinesine dönüştürür. Bu yapı, kod bir await ifadeye ulaştığında yürütmeyi duraklatma ve arka plan işi tamamlandığında yürütmeye devam etme gibi kodunuzda çeşitli işlemleri ve durumu takip eder.
Bilgisayar bilimi teorisi açısından, zaman uyumsuz programlama, Promise modelininzaman uyumsuzluk içerisindeki bir uygulamasıdır.
Zaman uyumsuz programlama modelinde, anlaşılması gereken birkaç temel kavram vardır:
- Hem G/Ç hem de CPU'ya bağlı kod için zaman uyumsuz kod kullanabilirsiniz, ancak uygulama farklıdır.
- Zaman uyumsuz kod, arka planda çalışmayı modellemek için
Task<T>veTasknesnelerini yapılar olarak kullanır. -
asyncanahtar sözcüğü, yöntem gövdesindeawaitanahtar sözcüğünü kullanmanıza olanak tanıyan bir yöntemi zaman uyumsuz bir yöntem olarak bildirir. -
awaitanahtar sözcüğünü uyguladığınızda kod çağırma yöntemini askıya alır ve görev tamamlanana kadar denetimi çağırana geri verir. -
awaitifadesini yalnızca zaman uyumsuz bir yöntemde kullanabilirsiniz.
G/Ç ile sınırlı örnek: Web hizmetinden veri indirme
Bu örnekte, kullanıcı bir düğme seçtiğinde uygulama bir web hizmetinden veri indirir. İndirme işlemi sırasında uygulamanın kullanıcı arabirimi iş parçacığını engellemek istemezsiniz. Aşağıdaki kod bu görevi gerçekleştirir:
s_downloadButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI as the request
// from the web service is happening.
//
// The UI thread is now free to perform other work.
var stringData = await s_httpClient.GetStringAsync(URL);
DoSomethingWithData(stringData);
};
Kod, Task nesneleriyle etkileşimde karmaşıklaşmadan amacı (verileri zaman uyumsuz olarak indirme) ifade eder.
CPU'ya bağlı örnek: Oyun hesaplaması çalıştırma
Sonraki örnekte, bir mobil oyun, bir düğme olayına yanıt olarak ekrandaki birkaç karaktere hasar verir. Hasar hesaplaması yapmak pahalı olabilir. Kullanıcı arabirimi iş parçacığında hesaplamanın çalıştırılması, hesaplama sırasında görüntüleme ve kullanıcı arabirimi etkileşimi sorunlarına neden olabilir.
Görevi işlemenin en iyi yolu, çalışmayı Task.Run yöntemiyle tamamlamak için bir arka plan işlem başlatmaktır. İşlem, bir await ifadesi kullanılarak sonuç verir. Görev tamamlandığında işlem sürdürülür. Bu yaklaşım, iş arka planda tamamlarken kullanıcı arabiriminin sorunsuz çalışmasını sağlar.
static DamageResult CalculateDamageDone()
{
return new DamageResult()
{
// Code omitted:
//
// Does an expensive calculation and returns
// the result of that calculation.
};
}
s_calculateButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI while CalculateDamageDone()
// performs its work. The UI thread is free to perform other work.
var damageResult = await Task.Run(() => CalculateDamageDone());
DisplayDamage(damageResult);
};
Kod, düğme Clicked olayının amacını açıkça ifade eder. Arka plan iş parçacığını manuel olarak yönetmek gerekmez ve görevi engellemeyen bir biçimde tamamlar.
CPU bağlı ve Girdi/Çıktı bağlı senaryoları tanıma
Önceki örneklerde G/Ç ve CPU'ya bağlı işler için async değiştirici ve await ifadesinin nasıl kullanılacağı gösterilmektedir. Her senaryo için bir örnek, işlemin bağlı olduğu yere göre kodun nasıl farklı olduğunu gösterir. Uygulamanıza hazırlanmak için bir işlemin G/Ç veya CPU'ya bağlı olduğunu belirlemeyi anlamanız gerekir. Uygulama seçiminiz kodunuzun performansını büyük ölçüde etkileyebilir ve olası olarak yapıların yanlış kullanılıp kullanılmamasına neden olabilir.
Kod yazmadan önce ele alınması gereken iki birincil soru vardır:
| Soru | Senaryo | Uygulama |
|---|---|---|
| Kod, veritabanındaki veriler gibi bir sonucu veya eylemi beklemeli mi? | G/Ç bağlı |
async değiştiriciyi ve await ifade olmadan kullanın. Görev Paralel Kitaplığı'nı kullanmaktan kaçının. |
| Kod pahalı bir hesaplama çalıştırmalıdır? | CPU'ya bağlı |
async değiştiricisini ve await ifadesini kullanın, ancak işi Task.Run yöntemiyle başka bir iş parçacığına aktarın. Bu yaklaşım, CPU yanıt hızıyla ilgili endişeleri ele alır. Çalışma eşzamanlılık ve paralellik için uygunsa, Görev Paralel Kitaplığı'nı da kullanmayı göz önünde bulundurun. |
Kodunuzun yürütülmesini her zaman ölçün. Çok iş parçacıklı çalışma sırasında, bağlam değişimlerinin maliyetine kıyasla CPU'ya bağlı çalışmanızın yeterince maliyetli olmadığını fark edebilirsiniz. Her seçimin dezavantajları vardır. Durumunuz için doğru dengeyi seçin.
Diğer örnekleri keşfedin
Bu bölümdeki örneklerde, C# dilinde zaman uyumsuz kod yazmanın çeşitli yolları gösterilmektedir. Karşılaşabileceğiniz birkaç senaryo ele alınıyor.
Ağdan veri ayıklama
Aşağıdaki kod, belirli bir URL'den HTML'yi indirir ve ".NET" dizesinin HTML'de kaç kez gerçekleştiğini sayar. Kod, görevi gerçekleştiren ve sayıyı döndüren bir Web API denetleyicisi yöntemi tanımlamak için ASP.NET kullanır.
Not
Üretim kodunda HTML ayrıştırma yapmayı planlıyorsanız normal ifadeleri kullanmayın. Bunun yerine ayrıştırma kütüphanesi kullanın.
[HttpGet, Route("DotNetCount")]
static public async Task<int> GetDotNetCountAsync(string URL)
{
// Suspends GetDotNetCountAsync() to allow the caller (the web server)
// to accept another request, rather than blocking on this one.
var html = await s_httpClient.GetStringAsync(URL);
return Regex.Matches(html, @"\.NET").Count;
}
Evrensel Windows Uygulaması için benzer kod yazabilir ve bir düğmeye bastıktan sonra sayma görevini gerçekleştirebilirsiniz:
private readonly HttpClient _httpClient = new HttpClient();
private async void OnSeeTheDotNetsButtonClick(object sender, RoutedEventArgs e)
{
// Capture the task handle here so we can await the background task later.
var getDotNetFoundationHtmlTask = _httpClient.GetStringAsync("https://dotnetfoundation.org");
// Any other work on the UI thread can be done here, such as enabling a Progress Bar.
// It's important to do the extra work here before the "await" call,
// so the user sees the progress bar before execution of this method is yielded.
NetworkProgressBar.IsEnabled = true;
NetworkProgressBar.Visibility = Visibility.Visible;
// The await operator suspends OnSeeTheDotNetsButtonClick(), returning control to its caller.
// This action is what allows the app to be responsive and not block the UI thread.
var html = await getDotNetFoundationHtmlTask;
int count = Regex.Matches(html, @"\.NET").Count;
DotNetCountLabel.Text = $"Number of .NETs on dotnetfoundation.org: {count}";
NetworkProgressBar.IsEnabled = false;
NetworkProgressBar.Visibility = Visibility.Collapsed;
}
Birden çok görev tamamlanmasını bekleyin
Bazı senaryolarda kodun birden çok veri parçasını eşzamanlı olarak alması gerekir.
Task API'leri, birden çok arka plan işi üzerinde engellemesiz bekleme gerçekleştiren zaman uyumsuz kod yazmanızı sağlayan yöntemler sağlar:
- Task.WhenAll yöntemi
- Task.WhenAny yöntemi
Aşağıdaki örnekte, bir dizi User nesnesi için userId nesne verilerini nasıl alabileceğiniz gösterilmektedir.
private static async Task<User> GetUserAsync(int userId)
{
// Code omitted:
//
// Given a user Id {userId}, retrieves a User object corresponding
// to the entry in the database with {userId} as its Id.
return await Task.FromResult(new User() { id = userId });
}
private static async Task<IEnumerable<User>> GetUsersAsync(IEnumerable<int> userIds)
{
var getUserTasks = new List<Task<User>>();
foreach (int userId in userIds)
{
getUserTasks.Add(GetUserAsync(userId));
}
return await Task.WhenAll(getUserTasks);
}
LINQ kullanarak bu kodu daha kısa yazabilirsiniz:
private static async Task<User[]> GetUsersByLINQAsync(IEnumerable<int> userIds)
{
var getUserTasks = userIds.Select(id => GetUserAsync(id)).ToArray();
return await Task.WhenAll(getUserTasks);
}
LINQ kullanarak daha az kod yazmanıza rağmen LINQ'yi zaman uyumsuz kodla karıştırırken dikkatli olun. LINQ ertelenmiş (veya gecikmeli) yürütme kullanır, yani hemen değerlendirme yapılmadan, dizi numaralandırılana kadar asenkron çağrılar gerçekleşmez.
LinQ sorgusunu Enumerable.ToArray hemen değerlendirmek ve görevleri bir dizide depolamak için yöntemini kullandığından, önceki örnek doğru ve güvenlidir. Bu yaklaşım, çağrıların hemen yürütülmesini id => GetUserAsync(id) ve döngü yaklaşımı gibi tüm görevlerin eşzamanlı olarak başlatılmasını foreach sağlar. Anında yürütmeyi ve eşzamanlı görev yürütmeyi sağlamak için her zaman LINQ ile görev oluştururken Enumerable.ToArray veya Enumerable.ToList kullanın. İşte, ToList() ve Task.WhenAny kullanarak görevleri tamamlandıklarında işlemek için bir örnek:
private static async Task ProcessTasksAsTheyCompleteAsync(IEnumerable<int> userIds)
{
var getUserTasks = userIds.Select(id => GetUserAsync(id)).ToList();
while (getUserTasks.Count > 0)
{
Task<User> completedTask = await Task.WhenAny(getUserTasks);
getUserTasks.Remove(completedTask);
User user = await completedTask;
Console.WriteLine($"Processed user {user.id}");
}
}
Bu örnekte, ToList() tamamlanan görevleri dinamik olarak kaldırmanıza olanak sağlayan, işlemi destekleyen Remove() bir liste oluşturur. Bu düzen, tüm görevlerin tamamlanmasını beklemek yerine, sonuçları kullanılabilir oldukları anda işlemek istediğinizde özellikle yararlıdır.
LINQ kullanarak daha az kod yazmanıza rağmen LINQ'yi zaman uyumsuz kodla karıştırırken dikkatli olun. LINQ ertelenen (veya gecikmeli) yürütmeyi kullanır. Zaman uyumsuz çağrılar, oluşturulan sırayı .ToList() veya .ToArray() yöntemine yapılan bir çağrıyla yinelemeye zorlamadığınız sürece, foreach döngüsünde olduğu gibi hemen gerçekleşmez.
Senaryonuza göre Enumerable.ToArray ile Enumerable.ToList arasında seçim yapabilirsiniz.
-
ToArray()veTask.WhenAllgibi tüm görevleri birlikte işlemeyi planladığınızda kullanın. Diziler, koleksiyon boyutunun sabit olduğu senaryolar için verimlidir. - Tamamlanan görevleri tamamlandıklarında koleksiyondan kaldırabileceğiniz yerler gibi
ToList()görevleri dinamik olarak yönetmeniz gerektiğinde kullanınTask.WhenAny.
Zaman uyumsuz programlama için dikkat edilmesi gereken noktaları gözden geçirin
Zaman uyumsuz programlamada, beklenmeyen davranışları önleyebilecek birkaç ayrıntı göz önünde bulundurulması gerekir.
async() yöntemi gövdesi içinde await komutunu kullanın
async değiştiricisini kullandığınızda, yöntem gövdesine bir veya daha fazla await ifadesi eklemeniz gerekir. Derleyici bir await ifadesiyle karşılaşmazsa, yöntem sonuç vermez. Derleyici bir uyarı oluştursa da, kod yine de derlenir ve derleyici yöntemini çalıştırır. C# derleyicisi tarafından zaman uyumsuz yöntem için oluşturulan durum makinesi hiçbir şey başarmadığından tüm işlem son derece verimsizdir.
Asenkron yöntem adlarına "Async" son ekini ekleyin.
.NET stil kuralı, tüm zaman uyumsuz yöntem adlarına "Async" sonekini eklemektir. Bu yaklaşım, zaman uyumlu ve zaman uyumsuz yöntemler arasında daha kolay ayrım yapmaya yardımcı olur. Kodunuz tarafından açıkça çağrılmamış bazı yöntemler (olay işleyicileri veya web denetleyicisi yöntemleri gibi) bu senaryoda geçerli olmayabilir. Bu öğeler kodunuz tarafından açıkça çağrılmadığından, açık adlandırma kullanmak o kadar önemli değildir.
Yalnızca olay işleyicilerinden 'async void' döndür
Olay işleyicilerinin void dönüş türleri bildirmesi gerekir ve diğer yöntemler gibi Task ve Task<T> nesneleri kullanamaz veya döndüremez. Zaman uyumsuz olay işleyicileri yazarken, işleyiciler için async dönüş yönteminde void değiştiricisini kullanmanız gerekir.
async void dönüş yöntemlerinin diğer uygulamaları TAP modelini izlemez ve zorluklar sunabilir:
-
async voidyöntemi içinde oluşan istisnalar bu yöntemin dışında yakalanamaz -
async voidyöntemleri test etmek zordur -
async voidyöntemleri, çağıran bunların eşzamansız olmasını beklemiyorsa negatif yan etkilere neden olabilir.
LINQ'te zaman uyumsuz lambdalarla dikkatli olun
LINQ ifadelerinde zaman uyumsuz lambdalar uygularken dikkatli olmanız önemlidir. LINQ'teki Lambda ifadeleri ertelenen yürütmeyi kullanır, bu da kodun beklenmeyen bir zamanda yürütülebileceği anlamına gelir. Bu senaryoya engelleyici görevlerin eklenmesi, kod doğru yazılmıyorsa kolayca kilitlenmeye neden olabilir. Ayrıca, zaman uyumsuz kodun iç içe yerleştirilmiş olması da kodun yürütülmesiyle ilgili mantık yürütmeyi zorlaştırabilir. Asenkron ve LINQ güçlüdür, ancak bu teknikler mümkün olduğunca dikkatli ve açık şekilde birlikte kullanılmalıdır.
Bloklamadan görevler için kontrolü devretmek
Programınız bir görevin sonucuna ihtiyaç duyuyorsa, await ifadesini engelleyici olmayan bir şekilde uygulayan kod yazın. Geçerli iş parçacığını bir Task öğesinin tamamlanmasını senkron olarak beklemek için engellemek, kilitlenmelere ve engellenen bağlam iş parçacıklarına neden olabilir. Bu programlama yaklaşımı daha karmaşık hata işleme gerektirebilir. Aşağıdaki tabloda, erişimin engelleyici olmayan bir yolla görevlerden nasıl sonuç alınıyor olduğu konusunda rehberlik sağlanmaktadır:
| Görev senaryosu | Geçerli kod | 'await' ile değiştirin |
|---|---|---|
| Arka plan görev sonucunu alma |
Task.Wait veya Task.Result |
await |
| Herhangi bir görev tamamlandığında devam et | Task.WaitAny |
await Task.WhenAny |
| Tüm görevler tamamlandığında devam | Task.WaitAll |
await Task.WhenAll |
| Bir süre sonra devam | Thread.Sleep |
await Task.Delay |
ValueTask türünü kullanmayı göz önünde bulundurun
Zaman uyumsuz bir yöntem Task nesnesi döndürdüğünde, belirli yollarda performans sorunları ortaya çıkabilir.
Task bir başvuru türü olduğundan, yığından bir Task nesnesi ayrılır.
async değiştiricisi ile bildirilen bir yöntem önbelleğe alınmış bir sonuç döndürürse veya zaman uyumlu olarak tamamlanırsa, ek ayırmalar kodun performans açısından kritik bölümlerinde önemli zaman maliyetleri tahakkuk edebilir. Ayırmalar sıkı döngülerde gerçekleştiğinde bu senaryo maliyetli hale gelebilir. Daha fazla bilgi için genelleştirilmiş zaman uyumsuz dönüş türleri bölümüne bakın.
ConfigureAwait(false) özelliğinin ne zaman ayarlandığını anlama
Geliştiriciler genellikle Task.ConfigureAwait(Boolean) boole değerinin ne zaman kullanılacağını sorgular. Bu API, Task örneğinin herhangi bir await ifadesini uygulayan durum makinesine bağlamı yapılandırmasına olanak tanır. Boole değeri doğru ayarlanmadığında performans düşebilir veya kilitlenmeler oluşabilir. Daha fazla bilgi için bkz. ConfigureAwait SSS.
Daha az durum odaklı kod yaz
Genel nesnelerin durumuna veya belirli yöntemlerin yürütülmesine bağlı kod yazmaktan kaçının. Bunun yerine, yalnızca yöntemlerin dönüş değerlerine bağlıdır. Daha az durumsal olan kod yazmanın birçok avantajı vardır.
- Kod hakkında daha kolay mantık yürütme
- Kodu test etmek daha kolay
- Eşzamansız ve eşzamanlı kodu karıştırmak daha kolay
- Kodda yarış durumlarından kaçınmak mümkün
- Dönüş değerlerine bağlı zaman uyumsuz kodu koordine etmek basit
- (Bonus) Koda bağımlılık ekleme ile iyi çalışır
Önerilen bir hedef, kodunuzda eksiksiz veya neredeyse eksiksiz Referans Şeffaflığı sağlamaktır. Bu yaklaşım tahmin edilebilir, test edilebilir ve sürdürülebilir bir kod tabanına neden olur.
Eşzamansız işlemlere eşzamanlı erişim
Senaryolarda, anahtar sözcük çağrı yığınınızda kullanılabilir olmadığında await zaman uyumsuz işlemleri engellemeniz gerekebilir. Bu durum, eski kod tabanlarında veya zaman uyumsuz yöntemleri değiştirilemeyen zaman uyumlu API'lere entegre ederken oluşur.
Uyarı
Zaman uyumsuz işlemlerde eşzamanlı engelleme, çıkmazlara yol açabilir ve mümkün olduğunca kaçınılmalıdır. Tercih edilen çözüm, çağrı yığınınız genelinde kullanmaktır async/await .
Bir Task üzerinde senkronize olarak engellemeniz gerektiğinde, en çok tercih edilenden en az tercih edilene kadar listelenen mevcut yaklaşımlar şunlardır:
- GetAwaiter() kullanın. GetResult()
- Karmaşık senaryolar için Task.Run kullanma
- Wait() ve Result kullan
GetAwaiter() kullanın. GetResult()
Model GetAwaiter().GetResult() genellikle zaman uyumlu bir şekilde engellemeniz gerektiğinde tercih edilen yaklaşımdır.
// When you cannot use await
Task<string> task = GetDataAsync();
string result = task.GetAwaiter().GetResult();
Bu yaklaşım:
- Özgün istisnayı bir
AggregateExceptioniçinde sarmalamadan korur. - Görev tamamlanana kadar şu anki iş parçacığını engeller.
- Dikkatli kullanılmadığı takdirde yine de kilitlenme riski taşır.
Karmaşık senaryolar için Task.Run kullanma
Zaman uyumsuz çalışmayı yalıtmanızı gereken karmaşık senaryolar için:
// Offload to thread pool to avoid context deadlocks
string result = Task.Run(async () => await GetDataAsync()).GetAwaiter().GetResult();
Bu desen:
- Bir iş parçacığı havuzu iş parçacığında zaman uyumsuz bir yöntemi yürütür.
- Bazı kilitlenme senaryolarını önlemeye yardımcı olabilir.
- Çalışmayı planlayarak iş parçacığı havuzuna ekstra yük ekler.
Wait() ve Result kullanma
Wait() ve Result öğelerini çağırarak bir engelleme yaklaşımı kullanabilirsiniz. Ancak, istisnaları AggregateException içinde sarmaladığı için bu yaklaşım önerilmez.
Task<string> task = GetDataAsync();
task.Wait();
string result = task.Result;
ve Wait()ile Result ilgili sorunlar:
- İstisnalar
AggregateExceptionile sarmalanır, bu da hata işlemeyi daha karmaşık hale getirir. - Daha yüksek kilitlenme riski.
- Kodda daha az net amaç.
Dikkat edilmesi gereken ek noktalar
- Kilitlenme önleme: Kullanıcı arabirimi uygulamalarında veya eşitleme bağlamı kullanırken özellikle dikkatli olun.
- Performans etkisi: İş parçacıklarını bloklamak ölçeklenebilirliği azaltır.
- Özel durum işleme: Özel durum davranışı desenler arasında farklılık gösterdiği için hata senaryolarını dikkatle test edin.
Zaman uyumsuz yöntemler için zaman uyumlu sarmalayıcıların zorlukları ve dikkate alınması gerekenler hakkında daha ayrıntılı bilgiler için bkz. Kapsama göre zaman uyumsuz yöntemler için zaman uyumlu sarmalayıcıları kullanıma sunmalı mıyım?.
Tüm örneği gözden geçirin
Aşağıdaki kod, Program.cs örnek dosyasında bulunan tam örneği temsil eder.
using System.Text.RegularExpressions;
using System.Windows;
using Microsoft.AspNetCore.Mvc;
class Button
{
public Func<object, object, Task>? Clicked
{
get;
internal set;
}
}
class DamageResult
{
public int Damage
{
get { return 0; }
}
}
class User
{
public bool isEnabled
{
get;
set;
}
public int id
{
get;
set;
}
}
public class Program
{
private static readonly Button s_downloadButton = new();
private static readonly Button s_calculateButton = new();
private static readonly HttpClient s_httpClient = new();
private static readonly IEnumerable<string> s_urlList = new string[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/aspnet/core",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/azure/devops",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/dotnet/desktop/wpf/get-started/create-app-visual-studio",
"https://learn.microsoft.com/education",
"https://learn.microsoft.com/shows/net-core-101/what-is-net",
"https://learn.microsoft.com/enterprise-mobility-security",
"https://learn.microsoft.com/gaming",
"https://learn.microsoft.com/graph",
"https://learn.microsoft.com/microsoft-365",
"https://learn.microsoft.com/office",
"https://learn.microsoft.com/powershell",
"https://learn.microsoft.com/sql",
"https://learn.microsoft.com/surface",
"https://dotnetfoundation.org",
"https://learn.microsoft.com/visualstudio",
"https://learn.microsoft.com/windows",
"https://learn.microsoft.com/maui"
};
private static void Calculate()
{
static DamageResult CalculateDamageDone()
{
return new DamageResult()
{
// Code omitted:
//
// Does an expensive calculation and returns
// the result of that calculation.
};
}
s_calculateButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI while CalculateDamageDone()
// performs its work. The UI thread is free to perform other work.
var damageResult = await Task.Run(() => CalculateDamageDone());
DisplayDamage(damageResult);
};
}
private static void DisplayDamage(DamageResult damage)
{
Console.WriteLine(damage.Damage);
}
private static void Download(string URL)
{
s_downloadButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI as the request
// from the web service is happening.
//
// The UI thread is now free to perform other work.
var stringData = await s_httpClient.GetStringAsync(URL);
DoSomethingWithData(stringData);
};
}
private static void DoSomethingWithData(object stringData)
{
Console.WriteLine($"Displaying data: {stringData}");
}
private static async Task<User> GetUserAsync(int userId)
{
// Code omitted:
//
// Given a user Id {userId}, retrieves a User object corresponding
// to the entry in the database with {userId} as its Id.
return await Task.FromResult(new User() { id = userId });
}
private static async Task<IEnumerable<User>> GetUsersAsync(IEnumerable<int> userIds)
{
var getUserTasks = new List<Task<User>>();
foreach (int userId in userIds)
{
getUserTasks.Add(GetUserAsync(userId));
}
return await Task.WhenAll(getUserTasks);
}
private static async Task<User[]> GetUsersByLINQAsync(IEnumerable<int> userIds)
{
var getUserTasks = userIds.Select(id => GetUserAsync(id)).ToArray();
return await Task.WhenAll(getUserTasks);
}
private static async Task ProcessTasksAsTheyCompleteAsync(IEnumerable<int> userIds)
{
var getUserTasks = userIds.Select(id => GetUserAsync(id)).ToList();
while (getUserTasks.Count > 0)
{
Task<User> completedTask = await Task.WhenAny(getUserTasks);
getUserTasks.Remove(completedTask);
User user = await completedTask;
Console.WriteLine($"Processed user {user.id}");
}
}
[HttpGet, Route("DotNetCount")]
static public async Task<int> GetDotNetCountAsync(string URL)
{
// Suspends GetDotNetCountAsync() to allow the caller (the web server)
// to accept another request, rather than blocking on this one.
var html = await s_httpClient.GetStringAsync(URL);
return Regex.Matches(html, @"\.NET").Count;
}
static async Task Main()
{
Console.WriteLine("Application started.");
Console.WriteLine("Counting '.NET' phrase in websites...");
int total = 0;
foreach (string url in s_urlList)
{
var result = await GetDotNetCountAsync(url);
Console.WriteLine($"{url}: {result}");
total += result;
}
Console.WriteLine("Total: " + total);
Console.WriteLine("Retrieving User objects with list of IDs...");
IEnumerable<int> ids = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
var users = await GetUsersAsync(ids);
foreach (User? user in users)
{
Console.WriteLine($"{user.id}: isEnabled={user.isEnabled}");
}
Console.WriteLine("Processing tasks as they complete...");
await ProcessTasksAsTheyCompleteAsync(ids);
Console.WriteLine("Application ending.");
}
}
// Example output:
//
// Application started.
// Counting '.NET' phrase in websites...
// https://learn.microsoft.com: 0
// https://learn.microsoft.com/aspnet/core: 57
// https://learn.microsoft.com/azure: 1
// https://learn.microsoft.com/azure/devops: 2
// https://learn.microsoft.com/dotnet: 83
// https://learn.microsoft.com/dotnet/desktop/wpf/get-started/create-app-visual-studio: 31
// https://learn.microsoft.com/education: 0
// https://learn.microsoft.com/shows/net-core-101/what-is-net: 42
// https://learn.microsoft.com/enterprise-mobility-security: 0
// https://learn.microsoft.com/gaming: 0
// https://learn.microsoft.com/graph: 0
// https://learn.microsoft.com/microsoft-365: 0
// https://learn.microsoft.com/office: 0
// https://learn.microsoft.com/powershell: 0
// https://learn.microsoft.com/sql: 0
// https://learn.microsoft.com/surface: 0
// https://dotnetfoundation.org: 16
// https://learn.microsoft.com/visualstudio: 0
// https://learn.microsoft.com/windows: 0
// https://learn.microsoft.com/maui: 6
// Total: 238
// Retrieving User objects with list of IDs...
// 1: isEnabled= False
// 2: isEnabled= False
// 3: isEnabled= False
// 4: isEnabled= False
// 5: isEnabled= False
// 6: isEnabled= False
// 7: isEnabled= False
// 8: isEnabled= False
// 9: isEnabled= False
// 0: isEnabled= False
// Application ending.