Skip to main content

csharp-async-patterns

Use when C# asynchronous programming with async/await, Task, ValueTask, ConfigureAwait, and async streams for responsive applications.

소스 정보

저장소
TheBushidoCollective/han
최근 소스 활동
2026년 2월 11일 17:47
감지된 SKILL.md 언어
영어
스타
198
포크
22

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
csharp-async-patterns
user-invocable
false
description
Use when C# asynchronous programming with async/await, Task, ValueTask, ConfigureAwait, and async streams for responsive applications.
allowed-tools
["Read","Write","Edit","Grep","Glob","Bash"]
# C# Async Patterns Asynchronous programming in C# enables writing responsive applications that efficiently handle I/O-bound and CPU-bound operations without blocking threads. The async/await pattern provides a straightforward way to write asynchronous code that looks and behaves like synchronous code. ## Async/Await Basics The `async` and `await` keywords transform synchronous-looking code into state machines that handle asynchronous operations. ```csharp using System; using System.Net.Http; using System.Threading.Tasks; public class AsyncBasics { // Basic async method public async Task<string> FetchDataAsync(string url) { using var client = new HttpClient(); // await suspends execution until response received string content = await client.GetStringAsync(url); return content; } // Async method without return value public async Task ProcessDataAsync() { await Task.Delay(1000); // Simulate async work Console.WriteLine("Processing complete"); } // Multiple awaits public async Task<int> CalculateSumAsync() { int value1 = await GetValueAsync(1); int value2 = await GetValueAsync(2); int value3 = await GetValueAsync(3); return value1 + value2 + value3; } private async Task<int> GetValueAsync(int id) { await Task.Delay(100); return id * 10; } // Async with exception handling public async Task<string> SafeFetchAsync(string url) { try { using var client = new HttpClient(); return await client.GetStringAsync(url); } catch (HttpRequestException ex) { Console.WriteLine($"Request failed: {ex.Message}"); return string.Empty; } } // Calling async methods public async Task DemoAsync() { // Await the result string data = await FetchDataAsync("https://api.example.com"); // Fire and forget (not recommended) _ = ProcessDataAsync(); // Wait for completion await ProcessDataAsync(); } } ``` ## Task and Task\<T\> `Task` represents an asynchronous operation and provides methods for composition, continuation, and error handling. ```csharp using System; using System.Threading; using System.Threading.Tasks; public class TaskExamples { // Creating tasks public void CreateTasks() { // Task.Run for CPU-bound work Task<int> task1 = Task.Run(() => { Thread.Sleep(1000); return 42; }); // Task.FromResult for already-known values Task<int> task2 = Task.FromResult(100); // Task.CompletedTask for void operations Task task3 = Task.CompletedTask; // TaskCompletionSource for manual control var tcs = new TaskCompletionSource<string>(); Task<string> task4 = tcs.Task; tcs.SetResult("Done"); } // Task composition public async Task<string> ComposeTasks() { // Sequential execution int result1 = await Task1Async(); int result2 = await Task2Async(result1); // Parallel execution Task<int> t1 = Task1Async(); Task<int> t2 = Task2Async(10); await Task.WhenAll(t1, t2); return $"Results: {t1.Result}, {t2.Result}"; } // Task.WhenAll - wait for all tasks public async Task<int[]> WhenAllExample() { var tasks = new[] { Task.Run(() => ComputeValue(1)), Task.Run(() => ComputeValue(2)), Task.Run(() => ComputeValue(3)) }; int[] results = await Task.WhenAll(tasks); return results; } // Task.WhenAny - wait for first task public async Task<int> WhenAnyExample() { var task1 = DelayedValue(1000, 1); var task2 = DelayedValue(2000, 2); var task3 = DelayedValue(500, 3); Task<int> completed = await Task.WhenAny(task1, task2, task3); return await completed; } // Cancellation support public async Task<string> CancellableOperation( CancellationToken cancellationToken) { for (int i = 0; i < 10; i++) { cancellationToken.ThrowIfCancellationRequested(); await Task.Delay(100, cancellationToken); Console.WriteLine($"Step {i + 1}"); } return "Completed"; } // Helper methods private Task<int> Task1Async() => Task.FromResult(10); private Task<int> Task2Async(int value) => Task.FromResult(value * 2); private int ComputeValue(int x) => x * x; private async Task<int> DelayedValue(int delay, int value) { await Task.Delay(delay); return value; } } ``` ## ValueTask and ValueTask\<T\> `ValueTask` provides better performance for operations that often complete synchronously, avoiding heap allocations. ```csharp using System; using System.Threading.Tasks; public class ValueTaskExamples { private readonly Dictionary<string, string> _cache = new Dictionary<string, string>(); // ValueTask for cached operations public ValueTask<string> GetValueAsync(string key) { // Synchronous path - no allocation if (_cache.TryGetValue(key, out string? value)) { return new ValueTask<string>(value); } // Asynchronous path return new ValueTask<string>(FetchFromDatabaseAsync(key)); } private async Task<string> FetchFromDatabaseAsync(string key) { await Task.Delay(100); // Simulate database call string value = $"Value for {key}"; _cache[key] = value; return value; } // Converting between Task and ValueTask public async ValueTask<int> ConversionExample() { // ValueTask from Task Task<int> task = GetTaskAsync(); ValueTask<int> valueTask = new ValueTask<int>(task); return await valueTask; } private Task<int> GetTaskAsync() => Task.FromResult(42); // ValueTask best practices public async Task ValueTaskUsageAsync() { // Good: await immediately string value1 = await GetValueAsync("key1"); // Bad: storing ValueTask // ValueTask<string> vt = GetValueAsync("key2"); // await vt; // First await // await vt; // Second await - WRONG! // Good: convert to Task if needed multiple times Task<string> task = GetValueAsync("key2").AsTask(); await task; await task; // OK with Task } // ConfigureAwait with ValueTask public async ValueTask ConfigureAwaitExample() { // Don't capture context (for library code) string value = await GetValueAsync("key") .ConfigureAwait(false); Console.WriteLine(value); } } ``` ## ConfigureAwait `ConfigureAwait` controls whether to capture the synchronization context, critical for library code and avoiding deadlocks. ```csharp using System; using System.Threading.Tasks; public class ConfigureAwaitExamples { // Library method - use ConfigureAwait(false) public async Task<string> LibraryMethodAsync() { // Don't capture synchronization context await Task.Delay(100).ConfigureAwait(false); // This continues on thread pool thread string result = await GetDataAsync() .ConfigureAwait(false); return result.ToUpper(); } // UI method - use default (or ConfigureAwait(true)) public async Task UpdateUIAsync() { string data = await LoadDataAsync(); // This continues on UI thread // Can safely update UI controls Console.WriteLine($"Data: {data}"); } // Avoiding deadlocks public class DeadlockExample { // This can deadlock in synchronous context public string BadSync() { // DON'T DO THIS return GetDataAsync().Result; // Deadlock! } // Fix with ConfigureAwait(false) public string GoodSync() { return GetDataAsync() .ConfigureAwait(false) .GetAwaiter() .GetResult(); } // Better: make it async public async Task<string> BestAsync() { return await GetDataAsync(); } } // Mixing contexts public async Task MixedContextAsync() { // Runs on captured context await Task.Delay(100); Console.WriteLine("On original context"); // Runs on thread pool await Task.Delay(100).ConfigureAwait(false); Console.WriteLine("On thread pool"); // Still on thread pool (ConfigureAwait effect persists) await Task.Delay(100); Console.WriteLine("Still on thread pool"); } private async Task<string> GetDataAsync() { await Task.Delay(100); return "data"; } private async Task<string> LoadDataAsync() { await Task.Delay(100); return "loaded data"; } } ``` ## Async Streams (IAsyncEnumerable) Async streams enable asynchronous iteration over sequences of data, perfect for streaming APIs and large datasets. ```csharp using System; using System.Collections.Generic; using System.Net.Http; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; public class AsyncStreamExamples { // Basic async stream public async IAsyncEnumerable<int> GenerateNumbersAsync( int count) { for (int i = 0; i < count; i++) { await Task.Delay(100); yield return i; } } // Consuming async stream public async Task ConsumeStreamAsync() { await foreach (int number in GenerateNumbersAsync(10)) { Console.WriteLine(number); } } // Async stream with cancellation public async IAsyncEnumerable<string> ReadLinesAsync( string filePath, [EnumeratorCancellation] CancellationToken cancellationToken = default) { using var reader = new System.IO.StreamReader(filePath); while (!reader.EndOfStream) { cancellationToken.ThrowIfCancellationRequested(); string? line = await reader.ReadLineAsync(); if (line != null) { yield return line; } } } // Filtering async stream public async IAsyncEnumerable<int> FilterEvenNumbersAsync( IAsyncEnumerable<int> source) { await foreach (int number in source) { if (number % 2 == 0) { yield return number; } } } // Transforming async stream public async IAsyncEnumerable<string> FormatNumbersAsync( IAsyncEnumerable<int> source) { await foreach (int number in source) { yield return $"Number: {number:D3}"; } } // Async stream from API public async IAsyncEnumerable<string> FetchPagesAsync( string baseUrl, int totalPages) { using var client = new HttpClient(); for (int page = 1; page <= totalPages; page++) { string url = $"{baseUrl}?page={page}"; string content = await client.GetStringAsync(url); yield return content; } } // Composing async streams public async Task ComposeStreamsAsync() { var numbers = GenerateNumbersAsync(20); var evens = FilterEvenNumbersAsync(numbers); var formatted = FormatNumbersAsync(evens); await foreach (string value in formatted) { Console.WriteLine(value); } } } ``` ## Parallel Async Operations Combining parallelism with async operations for maximum throughput. ```csharp using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; public class ParallelAsyncExamples { // Process items in parallel public async Task<List<string>> ProcessInParallelAsync( List<int> items) { var tasks = items.Select(async item => { await Task.Delay(100); return $"Processed {item}"; }); string[] results = await Task.WhenAll(tasks); return results.ToList(); } // Throttled parallel execution public async Task<List<string>> ThrottledParallelAsync( List<int> items, int maxConcurrency) { var semaphore = new SemaphoreSlim(maxConcurrency); var tasks = items.Select(async item => { await semaphore.WaitAsync(); try { await Task.Delay(100); return $"Processed {item}"; } finally { semaphore.Release(); } }); string[] results = await Task.WhenAll(tasks); return results.ToList(); } // Parallel.ForEachAsync (.NET 6+) public async Task ParallelForEachAsyncExample(List<int> items) { await Parallel.ForEachAsync( items, new ParallelOptions { MaxDegreeOfParallelism = 4 }, async (item, cancellationToken) => { await Task.Delay(100, cancellationToken); Console.WriteLine($"Processed {item}"); }); } // Batched processing public async Task<List<string>> BatchProcessAsync( List<int> items, int batchSize) { var results = new List<string>(); for (int i = 0; i < items.Count; i += batchSize) { var batch = items.Skip(i).Take(batchSize); var batchResults = await ProcessInParallelAsync( batch.ToList()); results.AddRange(batchResults); } return results; } } ``` ## Error Handling in Async Code Proper error handling is crucial for robust asynchronous applications. ```csharp using System; using System.Threading.Tasks; public class AsyncErrorHandling { // Basic try-catch public async Task<string> BasicErrorHandlingAsync() { try { return await RiskyOperationAsync(); } catch (InvalidOperationException ex) { Console.WriteLine($"Operation error: {ex.Message}"); return "default"; } catch (Exception ex) { Console.WriteLine($"Unexpected error: {ex.Message}"); throw; } } // AggregateException from WhenAll public async Task HandleMultipleErrorsAsync() { try { await Task.WhenAll( FailingTaskAsync("Task 1"), FailingTaskAsync("Task 2"), FailingTaskAsync("Task 3") ); } catch (Exception ex)
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기