120 likes | 356 Views
Async Clinic. Lucian Wischik, Mads Torgersen, Stephen Toub (MSFT) Stephen Cleary (MVP). Brainteasers. Symptom: Not Running A synchronously. async void button1_Click(…) { … await Task .Run (() => work()); … }.
E N D
Async Clinic Lucian Wischik, Mads Torgersen, Stephen Toub (MSFT)Stephen Cleary (MVP)
Symptom: Not Running Asynchronously async voidbutton1_Click(…) { …awaitTask.Run(() => work()); …} async voidbutton1_Click(…){ …await ScheduleAsync(work); …}asyncTaskScheduleAsync(Action work) { work(); } Problem: Thinking ‘async’ forks. Solution: Use Task.Run around a synchronous method if you need to offload… but don’t expose public APIs like this.
Symptom: Completing Too Quickly …;Task.Delay(1000);…; Problem: Neglecting to await. Solution: Await your awaitables. …;awaitTask.Delay(1000);…; Problem: Async void lambda. Solution: Use extreme caution (& knowledge) when passing around async lambdas as void-returning delegates. Parallel.For(0, 10, asynci => { awaitTask.Delay(1000);}); Problem: Task<Task> instead of Task. Solution: Make sure you’re unwrapping your Tasks, explicitly (Unwrap) or implicitly. awaitTask.Run(async() => { awaitTask.Delay(1000); }); awaitTask.Factory.StartNew( async() => { awaitTask.Delay(1000); });
Symptom: Method Not Completing asyncvoidbutton1_Click(…) {awaitFooAsync();}asyncTaskFooAsync() { awaitTask.Delay(1000).ConfigureAwait(false); } voidbutton1_Click(…) {FooAsync().Wait();}asyncTaskFooAsync() { awaitTask.Delay(1000); } asyncvoidbutton1_Click(…) {awaitFooAsync();}asyncTaskFooAsync() { awaitTask.Delay(1000); } Problem: Deadlocking UI thread. Solution: Don’t synchronously wait on the UI thread. Problem: Deadlocking UI thread. Solution: Use ConfigureAwait(false) whenever possible. vartcs = newTaskCompletionSource<T>();Task.Run(delegate { T result = Foo();tcs.SetResult(result);});returntcs.Task; vartcs = newTaskCompletionSource<T>();Task.Run(delegate {try { T result = Foo();tcs.SetResult(result); }catch(Exception e) { tcs.SetException(e); }});returntcs.Task; Problem: Awaited task never completes. Solution: Always complete Tasks when the underlying operation has ended. Always.
Throttling (1) asyncTaskLotsOfWorkAsync(){varthrottle = Throttle<string>(asyncmessage =>{awaitTask.Yield();Console.WriteLine(message);},maxParallelism: Environment.ProcessorCount); throttle.Post("lots"); throttle.Post("of");throttle.Post("work"); throttle.Complete();// Signal that we're done enqueuing work. awaitthrottle.Completion; } staticITargetBlock<T> Throttle<T>(Func<T, Task> worker, intmax){ varopts = newExecutionDataflowBlockOptions() {MaxDegreeOfParallelism = max}; returnnewActionBlock<T>(worker,opts); }
Throttling (2) publicasyncTask<List<House>> LoadHousesAsync(int first, int last) { varloadedHouses = newList<House>(); var queue = newQueue<int>(Enumerable.Range(first, last – first + 1)); // Throttle the rate of issuing requests... var worker1 = WorkerAsync(queue, loadedHouses); var worker2 = WorkerAsync(queue, loadedHouses); var worker3 = WorkerAsync(queue, loadedHouses); awaitTask.WhenAll(worker1, worker2, worker3); returnloadedHouses; } privateasyncTaskWorkerAsync(Queue<int> queue, List<House> results) { while (queue.Count > 0) { inti = queue.Dequeue(); var house = awaitHouse.LoadFromDatabaseAsync(i); results.Add(house); } }
Throttling (3) Explicitly Controlled await Task.WhenAll(fromiinEnumerable.Range(0, N) selectTask.Run(asyncdelegate { … })); SemaphoreSlim varsem = newSemaphoreSlim(N); awaitsem.WaitAsync(); …; sem.Release(); ConcurrentExclusiveSchedulerPair var f = newTaskFactory( newConcurrentExclusiveSchedulerPair(TaskScheduler.Default, N).ConcurrentScheduler); f.StartNew(…); // note: only counts tasks currently on a thread ActionBlock varab = newActionBlock<T>(async t => …, newExecutionDataflowBlockOptions { MaxDegreeOfParallelism = N });
Task-based Async Pattern (TAP) Signature Task<TResult> XxAsync( T1 Arg1, T2 Arg2, CancellationTokencancellationToken, IProgress<TProgress> progress); “Async” suffix Parameters matching synchronous method (no ref or out) Cancellation/progress parameters only if supported Returns Task or Task<TResult> Behavior Returns quickly to caller Returned Task must be “hot”May complete synchronously Exceptions stored in returned Task (only usage exceptions allowed to escape synchronously)
async & await are about joins, not forks publicstatic Task PausePrintAsync() { // Don’t consume threads // when no threads are needed vart = Task.Delay(10000); returnt.ContinueWith(_ => { Console.WriteLine("Hello")); }); } publicstatic Task PausePrintAsync() { // Requires a thread for all // of its processing returnTask.Run(() => { Thread.Sleep(10000); Console.WriteLine("Hello"); }); } Task Synchronous join Asynchronous join task.Wait(); awaittask;