Multithreading - Asynchronous Method Cancellation
This example explains how to implement support for asynchronous method
cancellation in your class. It is based on the Create an Asynchronous Method
example.
We will implement a CancelAsync method that signals the
asynchronous worker to cancel the operation. This is done by setting a
boolean variable that is periodically checked by the asynchronous
worker. When the asynchronous worker receives the cancel signal, it finishes and
fires the MyTaskCompleted event with the Cancelled
flag set.
MyAsyncContext class
To signal the worker, we need an object with a boolean property. This
property will be set by the CancelAsync method and periodically checked by the
asynchronous worker.
internal class MyAsyncContext { private readonly object _sync = new object(); private bool _isCancelling = false; public bool IsCancelling { get { lock (_sync) { return _isCancelling; } } } public void Cancel() { lock (_sync) { _isCancelling = true; } } }
We have to add a member of the MyAsyncContext type to our class.
Code:
Code:
private MyAsyncContext _myTaskContext = null;
This member is set in the MyTaskAsync method when the asynchronous operation
is invoked. The same MyAsyncContext instance is passed also to the asynchronous
worker.
private readonly object _sync = new object(); public void MyTaskAsync(string[] files) { MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(MyTaskWorker); AsyncCallback completedCallback = new AsyncCallback(MyTaskCompletedCallback); lock (_sync) { if (_myTaskIsRunning) throw new InvalidOperationException("The control is currently busy."); AsyncOperation async = AsyncOperationManager.CreateOperation(null); MyAsyncContext context = new MyAsyncContext(); bool cancelled; worker.BeginInvoke(files, context, out cancelled, completedCallback, async); _myTaskIsRunning = true; _myTaskContext = context; } }
Asynchronous code modifications
The asynchronous worker must be modified to support cancellation. As you
could notice in MyTaskAsync code, we added one input parameter of type
MyAsyncContext and one output boolean parameter. The
latter one indicates, whether the operation has been cancelled, when the worker
finishes.
private delegate void MyTaskWorkerDelegate(string[] files, MyAsyncContext asyncContext, out bool cancelled);
private void MyTaskWorker(string[] files, MyAsyncContext asyncContext, out bool cancelled) { cancelled = false; foreach (string file in files) { // a time consuming operation with a file (compression, encryption etc.) Thread.Sleep(1000); if (asyncContext.IsCancelling) { cancelled = true; return; } } }
The worker periodically test the
asyncContext.IsCancelling
property. If the property is set to true, the output parameter
cancelled
is set and the method is finished.
The value of the
cancelled
parameter is copied to the AsyncCompletedEventArgs.Cancelled
property in asynchronous operation completed callback.private void MyTaskCompletedCallback(IAsyncResult ar) { // get the original worker delegate and the AsyncOperation instance MyTaskWorkerDelegate worker = (MyTaskWorkerDelegate)((AsyncResult)ar).AsyncDelegate; AsyncOperation async = (AsyncOperation)ar.AsyncState; bool cancelled; // finish the asynchronous operation worker.EndInvoke(out cancelled, ar); // clear the running task flag lock (_sync) { _myTaskIsRunning = false; _myTaskContext = null; } // raise the completed event AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null, cancelled, null); async.PostOperationCompleted( delegate(object e) { OnMyTaskCompleted((AsyncCompletedEventArgs)e); }, completedArgs); }
CancelAsync method
This method signals the asynchronous worker to cancel the operation. It uses
the MyAsyncContext instance that was created during asynchronous operation
invokation.
public void CancelAsync() { lock (_sync) { if (_myTaskContext != null) _myTaskContext.Cancel(); } }
Note: If you have only one asynchronous method in your class, consider renaming the CancelAsync method to a name similar to MyTaskAsyncCancel. All the asynchronous operation related methods will then be displayed together in Visual Studio.
Comments
Post a Comment