The .NET Asynchrony And Multithreading lecture (in progress, not finished)
Plain Threading
Thread.Sleep(0) vs Thread.Yield()
Foreground and Background Threads
finally blocks in the execution stack of background threads are circumvented
(including using)
Thread Priority
Memory-mapped files
Exception Handling
Swallow of an exception in the main thread.
Centralized exception handling
global event
only for an UI thread
AppDomain.CurrentDomain.UnhandledException
app will shutdown or <legacyunhandledexceptionpolicy>
Thread Pooling
Thread.CurrentThread.IsThreadPoolThread
Oversubscription
hill-climbing algorithm
Optimizing the Thread Pool
How Does the Minimum Thread Count Work?
States
Synchronous
Thread-Local Storage - "TLS"
ThreadLocal<t> Class
ThreadStaticAttribute
Lazy<t> Class
Asyncronous
ExecutionContext - "EC"
built by AsyncTaskMethodBuilder
flowing EC
var ec = ExecutionContext.Capture();
ExecutionContext.Run(ec, delegate...
All async API do this implicitly
Stored in a Task object
EC is cache for the SecurityContext, HostExecutionContext, CallContext etc.
SynchronizationContext - "SC"
built by the support for awaiting Task and Task<tresult>
SynchronizationContext.Current.Post(delegate
Main classes
WindowsFormSynchronizationContext
DispatcherSynchronizationContext
SynchronizationContext (default; for the ThreadPool)
AspNetSynchronizationContext
The Schedulers in the Reactive Extensions is a more featured analog for SC.
+ ASP.NET subtle
aspnet:UseTaskFriendlySynchronizationContext
Presence of SC (capture of it) overrides default marshaling of a continuation delegate to the TreadPool
Presence of SC is disabled by the ConfigureAwait (false)
It calls "Avoiding excessive bouncing" optimization
SynchronizationContext vs ExecutionContext
a jump (marshaling) for SC vs a ambient for EC
SC is supressed by ConfigureAwait (false).
ConfigureAwait(false) on every await of a task is usefull for library implementers
EC is supressed ExecutionContext.SuppressFlow
EC can hold SC
There's quite a mess with it.
API design's mistake to hold the SC in the EC
Difference with the internal and public EC.Capture and EC.Run
The problem: a second async method will be executed on a Dispatcher
It cause a drawback for performance.
In short, SynchronizationContext.Current does not “flow” across C#'s "await" key word points.
But flows exists in public custom implementations.
Threading (blocking)
Raw & Old
Blocking
Simple blocking methods
Sleep(), someThread.Join, Task.Wait, EndInvoke
Blocking Versus Spinning
Locking constructs
Exclusive locking (Locking)
Monitor (lock)
lockTaken, TryEnter
Locking and Atomicity
Nested Locking
Deadlocks
OOP uncountable with the Parallel Programming
Dispatcher.Invoke
Mutex
50 times slower than a lock
prefix its name with Global
Nonexclusive locking
Semaphore
thread-agnostic
SemaphoreSlim
ReaderWriterLockSlim
Spinning
SpinLock and SpinWait
while (!proceed) Thread.Sleep (10);
Signaling constructs
Event Wait Handles
EventWaitHandle
AutoResetEvent
ManualResetEvent
ManualResetEventSlim
CountdownEvent
Monitor’s Wait/Pulse methods
ThreadPool.RegisterWaitForSingleObject
Barrier classes
WaitHandle
Set, WaitOne, and Reset
WaitAny, WaitAll, and SignalAndWait
Alternatives to WaitAll and SignalAndWait
Signaling with Wait and Puls by Monitor
Wait and Pulse pattern
Producer/Consumer Queue vs BlockingCollection<t>
Nonblocking synchronization constructs
Thread.MemoryBarrier()
Thread.VolatileRead, Thread.VolatileWrite
volatile keyword
VolatileRead and VolatileWrite approach
Interlocked class
Thread Safety approaches
Singleton with exclusive lock for access
Stateless approach
To subclass ContextBoundObject and apply the Synchronization attribute to the class
Static methods
Read-only thread safety
Activation model for stateless apps
Thread Affinity (Dispatcher, SC)
Invoke works synchronously
Immutable Objects
Challenges
Race Condition
I/O-bound versus compute-bound
I/O-bound operations
An operation that spends most of its time waiting for something to happen
an example is downloading a web page or calling Console.ReadLine
involve input or output
Thread.Sleep is also deemed I/O-bound
Delayed by I/O operations
compute-bound operations
an operation that spends most of its time performing CPU-intensive work
Delayed by intensive calculations
have a big processor's time consumption
course-grained vs fine-grained concurrency
course-grained concurrency
a single concurrent operation that spans many methods
BackgroundWorker as an example.
fine-grained concurrency
a sequence of small concurrent operations, in between which execution bounces to the UI thread.
excessively fine-grained asynchrony can hurt performance
by overhead
Obsolete Patterns
## Asynchronous Programming Model (APM)
Begin (with AsyncCallback), End prefixed methods + IAsyncResult
Task.Factory.FromAsync
Asynchronous delegates
It's the APM pattern
It has huge overhead
the Task replaces it completely
with Run and ContinueWith
## Event-Based Asynchronous Pattern (EAP)
Encapsulation of a asynchrony logic in a single object
with the exposing of a *Completed suffixes event.
BackgroundWorker
It's the general-purpose EAP implementation.
Created for rich-client apps.
Tasks
It's the main state-container for the asynchrony now in the .NET.
Manual threads approach lack
Wait
Multi long-running tasks
TaskCreationOptions.LongRunning
Others...
Exceptions
Convenient propagation
autonomous tasks
TaskScheduler.UnobservedTaskException
Continuations
GetResult
ConfigureAwait
ContinueWith
TaskContinuationOptions
TaskCompletionSource
Task.Delay
await and async
"await"
may be only inside "async" method.
Except: catch, finally, lock, unsafe context,
or an executable’s entry point (main method)
Captures context
All "async" methods returns the Task or Task<tresult>.
Except: Void-returning async methods have a specific purpose:
to make asynchronous event handlers possible.
Have lack with
erorr-handling.
An exception will be raised directly on the SynchronizationContext.
The catch block will be ignored.
See AppDomain.UnhandledException or similar for UI.
that is "recipe for unmaintainability"
Completion
can't be composed by using the await
useless for Task.WhenAny, Task.WhenAll and so on.
A workaround is a custom SC.
difficult for a testing
async methods may be static.
Asynchronous Lambda Expressions
unnamed async methods
async+return+compiler+task
event handlers
myButton.Click += async (sender, args) =>
WinRT - IAsyncInfo
IAsyncAction, IAsyncOperation<tresult>
AsTask, await
Asynchrony and Synchronization Contexts (SynchronizationContexts)
global exception posting with synchronization context
void vs a Task return
throw before await
same like the yield
ASP.NET - OperationStarted, OperationCompleted
Optimizations
Completing synchronously
Implicit return by compiler by TaskCompletionSource
someTask.GetAwaiter() -> awaiter.IsCompleted
small overhead for awaiting
Task.FromResult, returns an already-signaled task vs async without awaiting
Don't mark method async if it returns a Task from other async method
Avoiding excessive bouncing
ConfigureAwait (false);
synchronization context ignoring in a deep sync call stack
(especially for a loop with async call in it)
Cancellation
OperationCanceledException - predefined exception
CancellationTokenSource + CancellationToken - pattern implementation
WinRT follow an inferior protocol (pattern)
CancellationToken.Register - cancellation callback + disposing
IsCanceled vs IsFaulted
Progress Reporting
IProgress<t> and Progress<t>
synchronization context
WinRT IAsyncInfo...
.AsTask (cancelToken, progress);
The Task-based Asynchronous Pattern (TAP)
summary
Task Combinators
await Task.WhenAny(...)
await await - exceptions are then re-thrown without an AggregateException wrapping.
await task; - Unwrap result/re-throw
WhenAny with differently typed tasks gives untemplayed (untyped) task.
await Task.WhenAll(...)
Exception.InnerExceptions.Count
Task<tresult>
uris.Select (async uri => (await new WebClient().DownloadDataTaskAsync (uri)).Length);
Custom combinations + encapsulation
ContinueWith is more convenient than GetAwaiter().OnCompleted
Cache like approach (see above: Optimizations, Completing synchronously)
If a value cached async method returns synchronosly.
Task.FromResult<tresult>(result) for the Task<t> or Task.FromResult<object>(null) for Task.
Before .NET 4.5:
var source = new TaskCompletionSource<result>();
source.SetResult(theResult);
return source.Task;
Avoiding excessive bouncing (see above)
Tackle with the Async and the Threading when combined
Async (async await) <> Threading (blocks the current thread)
await <> Task.Wait, Task.Result, someThread.Join... (Wait backgrond operation or its result)
await Task.Delay <> Thread.Sleep (Wait a period of time)
await Task.Yield <> Thread.Yield
await Task.WhenAny <> Task.WaitAny, WaitHandle.WaitAny (Wait for any operation to complete)
await Task.WhenAll <> Task.WaitAll, WaitHandle.WaitAll (Retrieve the results of multiple tasks)
Task.Factory.StartNew is dangerous (too raw)
Deadlocks
var delayTask = DelayAsync();
delayTask.Wait();
Try to use the Context-free code (SC free)
await AsyncMethod().ConfigureAwait(continueOnCapturedContext: false);
Avoiding excessive bouncing (see above)
TPL Dataflow or Reactive Extensions
Literature
Comments
Post a Comment