most common mistakes in using tasks and in asynchronous …end5 start5 source: lucian wischik. cpu...
TRANSCRIPT
![Page 1: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/1.jpg)
Most Common Mistakes in Using Tasks and in
Asynchronous CodeJiří Činčura | @cincura_net
![Page 2: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/2.jpg)
Why asynchronous?
• Offloading• I.e. free UI thread
• Not all threads are equal
• Concurrency• Multiple operations at once
• Scalability• (not) wasting resources
![Page 3: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/3.jpg)
Why asynchronous?
• Asynchronous operations existed since stone age• BeginXxx, EndXxx (APM)
• EAP
• async/await is not about creating (from nothing) async methods…
• …but a way to compose/consume async methods
![Page 4: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/4.jpg)
![Page 5: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/5.jpg)
CPU bound vs IO bound operations
public List<Something> LoadSomething(){
var result = new List<Something>();for (var i = 1; i <= 5; i++){var s = Something.LoadFromNetwork(id: i);result.Add(s);
}return result;
}
![Page 6: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/6.jpg)
CPU bound vs IO bound operations
work1
work2
work3
work4
work5Source: Lucian Wischik
![Page 7: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/7.jpg)
CPU bound vs IO bound operations
3
response out300ms
work1 work2
work3 work4
work5
Parallel.For
Source: Lucian Wischik
![Page 8: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/8.jpg)
CPU bound vs IO bound operations
end1
start1
end2
start2
end3
start3
end4
start4
end5
start5
Source: Lucian Wischik
![Page 9: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/9.jpg)
CPU bound vs IO bound operations
end2
start1
start2start3start4start5
response out~100ms
end5
end1
end3end4
Source: Lucian Wischik
![Page 10: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/10.jpg)
CPU bound vs IO bound operations
• CPU bound• Parallel.For, Task.Run, …
• IO bound• async (real async code)
![Page 11: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/11.jpg)
![Page 12: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/12.jpg)
Async method runs on…
• … thread• True? False?
• … thread pool• True? False?
• False. False.• Scalability
• Asynchronous operations are performed by HW (then I/O completion port)
![Page 13: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/13.jpg)
Task.FromResult for “known” data
public Task<int> AddAsync(int a, int b){
return Task.Run(() => a + b);}
public Task<int> AddAsync(int a, int b){
return Task.FromResult(a + b);}
![Page 14: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/14.jpg)
Task.FromResult for “known” data
• Task is a reference type (=> on heap)
• ValueTask<T>
public ValueTask<int> AddAsync(int a, int b){
return new ValueTask<int>(a + b);}
![Page 15: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/15.jpg)
Long-running operations
• Long-running = background processing, sleep-wake
• Thread pool thread blocked• Injection solves it, but that doesn’t make it correct
• No TaskCreationOptions.LongRunning
• Creates a thread and first await destroys is
![Page 16: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/16.jpg)
![Page 17: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/17.jpg)
![Page 18: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/18.jpg)
await task === task.Wait();???
• .Wait() is blocking• Waiting for completions
• await jumps back here as soon as the operation is completed• Continuations and coroutines
![Page 19: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/19.jpg)
Task.Result and Task.Wait
• Sync over async
• Better call synchronous API directly
• Uses up to 2 threads• Blocked + callback
• Thread pool starvation
• Deadlocks via SynchronizationContext• Do not invent stuff
![Page 20: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/20.jpg)
Async must be everywhere
public int DoSomethingAsync(){
var result = CallDependencyAsync().Result;return result + 1;
}
public async Task<int> DoSomethingAsync(){
var result = await CallDependencyAsync();return result + 1;
}
![Page 21: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/21.jpg)
Async must be everywhere
• public string DoOperationBlocking()• {• return Task.Run(() => DoAsyncOperation()).Result;• }
• Blocking the thread that enters.• DoAsyncOperation will be scheduled on the default task scheduler,
and remove the risk of deadlocking.• In the case of an exception, this method will throw an
AggregateException wrapping the original exception.
![Page 22: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/22.jpg)
Async must be everywhere
• public string DoOperationBlocking2()• {• return Task.Run(() => DoAsyncOperation()).GetAwaiter().GetResult();• }
• Blocking the thread that enters.• DoAsyncOperation will be scheduled on the default task scheduler,
and remove the risk of deadlocking.
![Page 23: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/23.jpg)
Async must be everywhere
• public string DoOperationBlocking3()• {• return Task.Run(() => DoAsyncOperation().Result).Result;• }
• Blocking the thread that enters, and blocking the thead pool thread inside.
• In the case of an exception, this method will throw an AggregateException containing another AggregateException, containing the original exception
![Page 24: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/24.jpg)
Async must be everywhere
• public string DoOperationBlocking4()• {• return Task.Run(() => DoAsyncOperation().GetAwaiter().GetResult()).GetAwaiter().GetResult();
• }
• Blocking the thread that enters, and blocking the theadpool thread inside.
![Page 25: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/25.jpg)
Async must be everywhere
• public string DoOperationBlocking5()• {• return DoAsyncOperation().Result;• }
• Blocking the thread that enters.• No effort has been made to prevent a present SynchonizationContext
from becoming deadlocked.• In the case of an exception, this method will throw an
AggregateException wrapping the original exception.
![Page 26: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/26.jpg)
Async must be everywhere
• public string DoOperationBlocking6()• {• return DoAsyncOperation().GetAwaiter().GetResult();• }
• Blocking the thread that enters.• No effort has been made to prevent a present SynchonizationContext
from becoming deadlocked.
![Page 27: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/27.jpg)
Async must be everywhere
• public string DoOperationBlocking7()• {• var task = DoAsyncOperation();• task.Wait();• return task.GetAwaiter().GetResult();• }
• Blocking the thread that enters.• No effort has been made to prevent a present SynchonizationContext
from becoming deadlocked.
![Page 28: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/28.jpg)
![Page 29: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/29.jpg)
![Page 30: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/30.jpg)
Task.Result and Task.Wait (2)
• Constructorspublic class Service : IService{
readonly IRemoteConnection _connection;
public Service(IRemoteConnectionFactory connectionFactory){
_connection = connectionFactory.ConnectAsync().Result;}
}
• Factorypublic static async Task<Service> CreateAsync(IRemoteConnectionFactoryconnectionFactory){
return new Service(await connectionFactory.ConnectAsync());}
![Page 31: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/31.jpg)
Await instead of ContinueWith
• ContinueWith existed before await• I.e. ignores SynchronizationContext
public Task<int> DoSomethingAsync(){
return CallDependencyAsync().ContinueWith(task =>{return task.Result + 1;
});}
public async Task<int> DoSomethingAsync(){
var result = await CallDependencyAsync();return result + 1;
}
![Page 32: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/32.jpg)
TaskCompletionSource<T>
• Try/Set(Result/Exception/Canceled) runs inline
• Very dangerous• Re-entrancy, deadlocks, thread pool starvation, broken state, …
• TaskCreationOptions.RunContinuationsAsynchronously
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
var operation = new SomeOperation();operation.Completed += result => { tcs.SetResult(result); };return tcs.Task;
![Page 33: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/33.jpg)
Passing CancellationToken
public async Task<string> DoAsyncThing(CancellationToken cancellationToken = default){
var buffer = new byte[1024];var read = await _stream.ReadAsync(buffer, 0, buffer.Length);return Encoding.UTF8.GetString(buffer, 0, read);
}
public async Task<string> DoAsyncThing(CancellationToken cancellationToken = default){
var buffer = new byte[1024];var read = await _stream.ReadAsync(buffer, 0, buffer.Length,
cancellationToken);return Encoding.UTF8.GetString(buffer, 0, read);
}
![Page 34: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/34.jpg)
TimerQueue
• TimerQueue per CPU core
• Linked list of Timers
• Callbacks run on thread pool
• TimerQueue uses lock
• Disposing Timer removes it from TimerQueue
![Page 35: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/35.jpg)
CancellationTokenSource for timeouts
public async Task<Stream> HttpClientAsyncWithCancellationBad() {var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));using (var client = _httpClientFactory.CreateClient()) {var response = await client.GetAsync("http://backend/api/1", cts.Token);return await response.Content.ReadAsStreamAsync();
}}
public async Task<Stream> HttpClientAsyncWithCancellationGood() {using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))) {using (var client = _httpClientFactory.CreateClient()) {var response = await client.GetAsync("http://backend/api/1", cts.Token);return await response.Content.ReadAsStreamAsync();
}}
}
![Page 36: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/36.jpg)
Timeout Taskpublic static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout) {var delayTask = Task.Delay(timeout);var resultTask = await Task.WhenAny(task, delayTask);if (resultTask == delayTask) {throw new OperationCanceledException();
}return await task;
}
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout) {using (var cts = new CancellationTokenSource()) {var delayTask = Task.Delay(timeout, cts.Token);var resultTask = await Task.WhenAny(task, delayTask);if (resultTask == delayTask) {throw new OperationCanceledException();
} else {cts.Cancel();
}return await task;
}}
![Page 37: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/37.jpg)
FlushAsync for Stream/StreamWriter
using (var streamWriter = new StreamWriter(s)){
await streamWriter.WriteAsync("Hello World");}
using (var streamWriter = new StreamWriter(s)){
await streamWriter.WriteAsync("Hello World");await streamWriter.FlushAsync();
}
![Page 38: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/38.jpg)
Timer callbacks
public class Pinger{readonly Timer _timer;readonly HttpClient _client;
public Pinger(HttpClient client){_client = client;_timer = new Timer(Heartbeat, null, 1000, 1000);
}
public async void Heartbeat(object state){await _client.GetAsync("http://mybackend/api/ping");
}}
![Page 39: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/39.jpg)
Timer callbacks (2)
public class Pinger{readonly Timer _timer;readonly HttpClient _client;
public Pinger(HttpClient client){_client = client;_timer = new Timer(Heartbeat, null, 1000, 1000);
}
public void Heartbeat(object state){_ = DoAsyncPing();
}
private async Task DoAsyncPing(){await _client.GetAsync("http://mybackend/api/ping");
}}
![Page 40: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/40.jpg)
Implicit async void
public class BackgroundQueue{
public static void FireAndForget(Action action) { }}
BackgroundQueue.FireAndForget(async () => { await … });
public class BackgroundQueue{
public static void FireAndForget(Action action) { }public static void FireAndForget(Func<Task> action) { }
}
![Page 41: Most Common Mistakes in Using Tasks and in Asynchronous …end5 start5 Source: Lucian Wischik. CPU bound vs IO bound operations end2 start1 start2 start3 start4 start5 response out](https://reader034.vdocuments.us/reader034/viewer/2022052617/60ad812bc161ab3d4f6db5c2/html5/thumbnails/41.jpg)
ConcurrentDictionary.GetOrAdd
static ConcurrentDictionary<int, Person> _cache;var person = _cache.GetOrAdd(id, k => db.People.FindAsync(k).Result);
static ConcurrentDictionary<int, Task<Person>> _cache;var person = _cache.GetOrAdd(id, k => db.People.FindAsync(k));
static ConcurrentDictionary<int, Lazy<Task<Person>>> _cache;var person = await _cache.GetOrAdd(id, k => new Lazy<Task<Person>>(() => db.People.FindAsync(k), …)).Value;