A set of cases may arise when you use WaitHandle and you want to use that with the cancellation, the biggest problem with that is when a method is blocked, you cannot check the Cacelleation Token in a timely manner, this will instruct you how to use WaitHandle together with the Cancellation.
If you are working with old wait handle, such as the ManualResetEvent, because the ManualRestEvent is that it does not support the unified cancellation, you can program as such ...
class CancelOldStyleEvents { // Old-style MRE that doesn't support unified cancellation. -- we will see new Style ones, the XXXSlims ones static ManualResetEvent mres = new ManualResetEvent(false); public static void TestCancelNewStyleEvents() { var cts = new CancellationTokenSource(); // Pass the same token source to the delegate and to the task instance. Task.Run(() => DoWork(cts.Token), cts.Token); Console.WriteLine("Press s to start/restart, p to pause, or c to cancel."); Console.WriteLine("Or any other key to exit."); // New-style UI thread. bool goAgain = true; while (goAgain) { char ch = Console.ReadKey().KeyChar; switch (ch) { case 'c': // Token can only be canceled once. cts.Cancel(); break; case 'p': // - boqwang: pause the work mres.Reset(); break; case 's': mres.Set(); break; default: goAgain = false; break; } Thread.Sleep(100); } } static void DoWork(CancellationToken token) { while (true) { int eventThatSignaled = WaitHandle.WaitAny(new WaitHandle[] { mres, token.WaitHandle}, new TimeSpan(0, 0, 20)); if (eventThatSignaled == 1) // -- this means the mres is signaled? { Console.WriteLine("The wait operation was canceled."); // throw event canceld throw new OperationCanceledException(token); // -- imitate the cancelation operation } else if (token.IsCancellationRequested) { Console.WriteLine("I was canceled while running."); token.ThrowIfCancellationRequested(); } else if (eventThatSignaled == WaitHandle.WaitTimeout) { Console.WriteLine("I Timedout ."); break; // wait for another round } else { Console.Write("Working... "); // Simulating work. Thread.SpinWait(5000000); } } } }There are four situation that the DoWork function may goes under
1. In WaitHandle.WaitAny method, the token can instantiate the cancel operation , which cancel the wait operation prematurraly. In this situation, the mre is not set, which means the real work inside DoWork is not running.
2. The WaitHandle.WaitAny returns which, and it falls to the second branch, which means that it is allow to run, and the token does not instantiate a cancel operation; but the token has a cancel request, which means that it is now sending a cancel operation while the real work is still running, it is cancelling a running operation.
3. Timeout branch, if we don't have a signal and it times out , which means the apps is not running, and user does not cancel, instead there is no action, then we don't get response within defined time range so we can safely time out (it is like a default action branch when no response has been received after certain period of time)
4. the fourth branch, it is allowed to run, and no cancellation, so this part is where the real work is carried out .
now that we have check the old style WaitHandle, let check out how we program against the new Style, here is the code.
class CancelNewStyleEvents { // New-style MRESlim that supports unified cancellation // in its Wait methods. static ManualResetEventSlim mres = new ManualResetEventSlim(false); public static void TestCancelNewStyleEvents() { var cts = new CancellationTokenSource(); // Pass the same token source to the delegate and to the task instance. Task.Run(() => DoWork(cts.Token), cts.Token); Console.WriteLine("Press c to cancel, p to pause, or s to start/restart,"); Console.WriteLine("or any other key to exit."); // New-style UI thread. bool goAgain = true; while (goAgain) { char ch = Console.ReadKey().KeyChar; switch (ch) { case 'c': // Token can only be canceled once. cts.Cancel(); break; case 'p': mres.Reset(); break; case 's': mres.Set(); break; default: goAgain = false; break; } Thread.Sleep(100); } } static void DoWork(CancellationToken token) { while (true) { if (token.IsCancellationRequested) { Console.WriteLine("Canceled while running."); token.ThrowIfCancellationRequested(); } try { mres.Wait(token); // you should run Wait(Token), rather than the Wait() method mere } catch (OperationCanceledException) // -- or do we have { Console.WriteLine("The wait operation has been canceld"); throw; } Console.Write("Working..."); // Simulating work. Thread.SpinWait(500000); } } }
As you can see that XXXSlim has this Wait(CancellationToken) method that will throw OperationCancelException when the token is cancelled.