托管线程取消的几种方式
程序员文章站
2022-05-27 23:01:47
...
1、以协作方式取消线程
using System; using System.Threading; public class ServerClass { public static void StaticMethod(object obj) { CancellationToken ct = (CancellationToken)obj; Console.WriteLine("ServerClass.StaticMethod is running on another thread."); // Simulate work that can be canceled. while (!ct.IsCancellationRequested) { Thread.SpinWait(50000); } Console.WriteLine("The worker thread has been canceled. Press any key to exit."); Console.ReadKey(true); } } public class Simple { public static void Main() { // The Simple class controls access to the token source. CancellationTokenSource cts = new CancellationTokenSource(); Console.WriteLine("Press 'C' to terminate the application...\n"); // Allow the UI thread to capture the token source, so that it // can issue the cancel command. Thread t1 = new Thread(() => { if (Console.ReadKey(true).KeyChar.ToString().ToUpperInvariant() == "C") cts.Cancel(); } ); // ServerClass sees only the token, not the token source. Thread t2 = new Thread(new ParameterizedThreadStart(ServerClass.StaticMethod)); // Start the UI thread. t1.Start(); // Start the worker thread and pass it the token. t2.Start(cts.Token); t2.Join(); cts.Dispose(); } } // The example displays the following output: // Press 'C' to terminate the application... // // ServerClass.StaticMethod is running on another thread. // The worker thread has been canceled. Press any key to exit.
2、通过轮询侦听取消请求
using System; using System.Threading; using System.Threading.Tasks; public struct Rectangle { public int columns; public int rows; } class CancelByPolling { static void Main() { var tokenSource = new CancellationTokenSource(); // Toy object for demo purposes Rectangle rect = new Rectangle() { columns = 1000, rows = 500 }; // Simple cancellation scenario #1. Calling thread does not wait // on the task to complete, and the user delegate simply returns // on cancellation request without throwing. Task.Run(() => NestedLoops(rect, tokenSource.Token), tokenSource.Token); // Simple cancellation scenario #2. Calling thread does not wait // on the task to complete, and the user delegate throws // OperationCanceledException to shut down task and transition its state. // Task.Run(() => PollByTimeSpan(tokenSource.Token), tokenSource.Token); Console.WriteLine("Press 'c' to cancel"); if (Console.ReadKey(true).KeyChar == 'c') { tokenSource.Cancel(); Console.WriteLine("Press any key to exit."); } Console.ReadKey(); tokenSource.Dispose(); } static void NestedLoops(Rectangle rect, CancellationToken token) { for (int x = 0; x < rect.columns && !token.IsCancellationRequested; x++) { for (int y = 0; y < rect.rows; y++) { // Simulating work. Thread.SpinWait(5000); Console.Write("{0},{1} ", x, y); } // Assume that we know that the inner loop is very fast. // Therefore, checking once per row is sufficient. if (token.IsCancellationRequested) { // Cleanup or undo here if necessary... Console.WriteLine("\r\nCancelling after row {0}.", x); Console.WriteLine("Press any key to exit."); // then... break; // ...or, if using Task: // token.ThrowIfCancellationRequested(); } } } }
3、注册取消请求的回调 token.Register(() => wc.CancelAsync())
using System; using System.Net; using System.Threading; using System.Threading.Tasks; class CancelWithCallback { static void Main() { var cts = new CancellationTokenSource(); var token = cts.Token; // Start cancelable task. Task t = Task.Run( () => { WebClient wc = new WebClient(); // Create an event handler to receive the result. wc.DownloadStringCompleted += (obj, e) => { // Check status of WebClient, not external token. if (!e.Cancelled) { Console.WriteLine("The download has completed:\n"); Console.WriteLine(e.Result + "\n\nPress any key."); } else { Console.WriteLine("The download was canceled."); } }; // Do not initiate download if the external token // has already been canceled. if (!token.IsCancellationRequested) { // Register the callback to a method that can unblock. using (CancellationTokenRegistration ctr = token.Register(() => wc.CancelAsync())) { Console.WriteLine("Starting request\n"); wc.DownloadStringAsync(new Uri("http://www.contoso.com")); } } }, token); Console.WriteLine("Press 'c' to cancel.\n"); char ch = Console.ReadKey().KeyChar; Console.WriteLine(); if (ch == 'c') cts.Cancel(); Console.WriteLine("Press any key to exit."); Console.ReadKey(); cts.Dispose(); } }
4、侦听具有等待句柄的取消请求
using System; using System.Threading; using System.Threading.Tasks; class CancelOldStyleEvents { // Old-style MRE that doesn't support unified cancellation. static ManualResetEvent mre = new ManualResetEvent(false); static void Main() { 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."); // Old-style UI thread. bool goAgain = true; while (goAgain) { char ch = Console.ReadKey(true).KeyChar; switch (ch) { case 'c': cts.Cancel(); break; case 'p': mre.Reset(); break; case 's': mre.Set(); break; default: goAgain = false; break; } Thread.Sleep(100); } cts.Dispose(); } static void DoWork(CancellationToken token) { while (true) { // 没有收到信号 将等待 int eventThatSignaledIndex = WaitHandle.WaitAny(new WaitHandle[] { mre, token.WaitHandle }, new TimeSpan(0, 0, 20)); // Were we canceled while waiting? if (eventThatSignaledIndex == 1) { Console.WriteLine("The wait operation was canceled."); throw new OperationCanceledException(token); } // Were we canceled while running? else if (token.IsCancellationRequested) { Console.WriteLine("I was canceled while running."); token.ThrowIfCancellationRequested(); } // Did we time out? else if (eventThatSignaledIndex == WaitHandle.WaitTimeout) { Console.WriteLine("I timed out."); break; } else { Console.Write("Working... "); // Simulating work. Thread.SpinWait(5000000); } } } }
5、侦听多个取消请求
此示例展示了如何同时侦听两个取消令牌,以便在其中任意一个令牌发出请求时取消操作。
//创建一个新token 合并内部和外部的tokens
CancellationTokenSource linkedCts =
CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken)
using System; using System.Threading; using System.Threading.Tasks; class LinkedTokenSourceDemo { static void Main() { WorkerWithTimer worker = new WorkerWithTimer(); CancellationTokenSource cts = new CancellationTokenSource(); // Task for UI thread, so we can call Task.Wait wait on the main thread. Task.Run(() => { Console.WriteLine("Press 'c' to cancel within 3 seconds after work begins."); Console.WriteLine("Or let the task time out by doing nothing."); if (Console.ReadKey(true).KeyChar == 'c') cts.Cancel(); }); // Let the user read the UI message. Thread.Sleep(1000); // Start the worker task. Task task = Task.Run(() => worker.DoWork(cts.Token), cts.Token); try { task.Wait(cts.Token); } catch (OperationCanceledException e) { if (e.CancellationToken == cts.Token) Console.WriteLine("Canceled from UI thread throwing OCE."); } catch (AggregateException ae) { Console.WriteLine("AggregateException caught: " + ae.InnerException); foreach (var inner in ae.InnerExceptions) { Console.WriteLine(inner.Message + inner.Source); } } Console.WriteLine("Press any key to exit."); Console.ReadKey(); cts.Dispose(); } } class WorkerWithTimer { CancellationTokenSource internalTokenSource = new CancellationTokenSource(); CancellationToken internalToken; CancellationToken externalToken; Timer timer; public WorkerWithTimer() { internalTokenSource = new CancellationTokenSource(); internalToken = internalTokenSource.Token; // A toy cancellation trigger that times out after 3 seconds // if the user does not press 'c'. timer = new Timer(new TimerCallback(CancelAfterTimeout), null, 3000, 3000); } public void DoWork(CancellationToken externalToken) { // Create a new token that combines the internal and external tokens. //创建一个新token 合并内部和外部的tokens this.internalToken = internalTokenSource.Token; this.externalToken = externalToken; using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken)) { try { DoWorkInternal(linkedCts.Token); } catch (OperationCanceledException) { if (internalToken.IsCancellationRequested) { Console.WriteLine("Operation timed out."); } else if (externalToken.IsCancellationRequested) { Console.WriteLine("Cancelling per user request."); externalToken.ThrowIfCancellationRequested(); } } } } private void DoWorkInternal(CancellationToken token) { for (int i = 0; i < 1000; i++) { if (token.IsCancellationRequested) { // We need to dispose the timer if cancellation // was requested by the external token. timer.Dispose(); // Throw the exception. token.ThrowIfCancellationRequested(); } // Simulating work. Thread.SpinWait(7500000); Console.Write("working... "); } } public void CancelAfterTimeout(object state) { Console.WriteLine("\r\nTimer fired."); internalTokenSource.Cancel(); timer.Dispose(); } }
上一篇: 使用反射 快速访问属性