This post I’d like to dedicate to reviewing functionality of Async module – creating and manipulating async computations.
All functions can be divided into the following groups:
Auxiliary functions used in review
All code in the post was tested in FSI. Disclaimer: samples are pure-synthetic, all coincidences with real-life problems are accidental.
Functions, that run computations
"async" builder just describes asynchronous computations without running it so somebody has to do the first step and start execution. Async module provides several functions that accept definition of async workflow and run it.
- Async.Start – starts execution of the workflow using thread from thread pool
- Async.StartImmediate – starts execution of the given workflow in a current thread
- Async.RunSynchronously – starts execution of the given workflow and waits its completion
- Async.StartWithContinuations – - starts execution of the given workflow and after its completion passes result/exception/OperationCancelledException to one of specified functions. If thread, that initiates executes has SynchronizationContext installed, then final continuations will use this SynchronizationContext for posting results
- Async.StartAsTask – starts execution of the given workflow and returns System.Threading.Tasks.Task object that can be used to monitor execution status and receive result. Task should be treated in the same way as a task obtained from TaskCompletionSource.Task property – it is not execute the action itself and can be used for checking status and getting result. This function can be used to for interoperability between F# and other languages
Receiving results
Async.Start and Async.StartImmediate execute computations asynchronously so computation process should itself define ways for communication and returning final result. Async.RunSynchronously waits till workflow is finished and returns its result. Async.StartWithContinuations takes continuation for result as one of arguments. When using Async.StartAsTask, result can be taken in many ways (i.e. Task.ContinueWith). For more information please refer official TPL documentation.
Code samples
up to menuFunctions, that construct computations
Computation can be defined not only with async {} builder syntax. It is also possible to create them with the two functions of Async module.- Async.FromContinuations – this function accepts another function f with a tuple of 3 continuations as argument: success continuation, error continuation and cancel continuation and returns computation with f as a body. F can contain arbitrary code, but result/error and cancellation should be reported through appropriate continuations. If no continuations were invoked –then this shall be the final step of the workflow
- Async.FromBeginEnd – Begin/End+IAsyncResult is a common pattern for running asynchronous operations in .NET. FromBeginEnd acts as an adapter for async workflow interface by wrapping provided Begin/End method. Thus it allows using large number of existing components that supports asynchronous mode of work
Code samples
up to menuCancellation-related functions
Possibility to cancel task or a sequence of tasks is very important when dealing with asynchronous operations. Async module use the same approach as TPL – usage of CancellationTokenSource/CancellationToken. Async module contains default instance of CancellationToken that all executing opetations share by default, but all operations from “running computation” category accept personal CancellationToken as an optional parameter. Using combination of CancellationTokenSource/CancellationToken is widely covered in the ParallelExtension samples. Library code performs intermediate cancel request checks so developer doesn’t need to do the same in workflow definition. Other functions used in workflow may accept cancellation token as an argument and do checks explicitly. Cancellation related API in Async module provides access to default cancellation token and to token assigned to workflow.
- Async.DefaultCancellationToken - returns default cancellation token
- Async.CancelDefaultToken - signals cancellation on default token. Usually used outside the workflow
- Async.CancellationToken - get the async computation that returns cancellation token for the current workflow. This function is usually used inside workflow to run operations that should be cancelled simultaneously with the workflow
- Async.OnCancel - gets the computation that installs a cancellation handler.Handler will be executed if cancellation request is signaled. OnCancel returns Async<IDisposable> so common usage is use! _ = Async.OnCancel(..) or let! handler = Async.OnCancel(…) ... handler.Dispose()
Code samples
up to menuCreating adapters for interoperability with other languages
Often it is very useful to consume workflow functionality outside F#. Async.AsBeginEnd gives possibility to expose workflow as a triple of methods (Begin/End/Cancel) so it can be easily used from other .NET languages. In one of my next posts I’ll show how to extend Async with method that creates event-based adapter for workflow.
Code samples
up to menuAsync module function that are essential for tasks coordination
- Async.AwaitEvent - creates a computation that subscribes on given event and resumes execution when event is raised. Returns instance derived from EventArgs
- Async.AwaitIAsyncResult - creates a computation that waits on provided IAsyncResult. Returns true if IAsyncResult issues a signal within given timeout. Timeout parameter is optional, default value is -1 of Timeout.Infinite
- Async.AwaitWaitHandle - creates computation that waits on provided WaitHandle. Returns true if handle issues a signal within given timeout. Timeout parameter is optional, default value is -1 of Timeout.Infinite
- Async.AwaitTask - creates computation that waits on provided task and returns its result
One method is a bit special here - Async.Sleep.It doesn’t wait on anything, instead it allows pausing execution and resuming it on timeout. This operation will not block any operation system threads, but use System.Threading.Timer for scheduling
Code samples
up to menuThis term is arguable, and most likely it will be changed :) With it I'd like to define functions that manipulate over one or many async computations and produce new enhanced computation.
- Async.Parallel - – takes a sequence of async computation and returns workflow, that will execute source computation in a fork-join way. Results array of results
- Async.Catch - takes a computation and returns workflow that intercepts all exceptions within source computation. Result – Choice<’T, exn> it can be extracted via pattern matching
- Async.Ignore - takes a computation and returns workflow that executes source computation, ignores its result and returns unit
- Async.TryCancelled - takes a computation and a compensating action. Returns workflow that runs source computation. If execution is cancelled before completion, then proceed with compensating action
Code samples
up to menuFunctions that start subcomputation
Async workflow may start subworkflows that are executed simultaneously with the parent.
- Async.StartChild - produces a computation that starts a given computation and returns computation for receiving result (yes, Async<Async<…>>
<…> :) ). If parent computation requests the result and child computation is not finished yet – parent computation is suspended until child completion - Async.StartChildAsTask - produces a computation that starts a given computation and returns Task instance for monitoring status and receiving results
Code samples
up to menuSometimes it is necessary to specify explicitly what kind of thread shall be used for the next step of computations.
- Async.SwitchToThreadPool - creates a computation that executes its continuation in that thread from thread pool
- Async.SwitchToNewThread - creates a computation that executes its continuation in the new thread
- Async.SwitchToContext - creates a computation that with post its continuation to the given syncContext
Code samples
up to menu
I'm a little confused with
ОтветитьУдалитьdo! Async.SwitchToContext testSyncContext
I see it called SynchronizationContext.Post, but aside from that, it continues in the same thread. So where did it switch to?
it is because, test implementation of SynchronizationContext just prints debug info without switching threads.
ОтветитьУдалитьSmall sample: http://pastebin.com/ytB97dsG
ОтветитьУдалитьThanks. I thought there was a default implementation in the base class. Now i see you have to use WindowsFormsSynchronizationContext or implement your own derived class with some message passing mechanism.
ОтветитьУдалить