C#中 单线程使用 CancellationTokenSource 进行线程管理
1. Xaml 代码
<Grid Margin="20"><StackPanel Width="150" HorizontalAlignment="Left"><ButtonMargin="0,10,0,10"Click="BtnClick_StartTask"Content="开始任务"Style="{StaticResource BtnStyle}" /><ButtonMargin="0,10,0,10"Click="BtnClick_StopTask"Content="停止任务"Style="{StaticResource BtnStyle}" /><ButtonMargin="0,10,0,10"Click="BtnClick_PauseTask"Content="暂停任务"Style="{StaticResource BtnStyle}" /><ButtonMargin="0,10,0,10"Click="BtnClick_ResumTask"Content="恢复任务"Style="{StaticResource BtnStyle}" /></StackPanel>
</Grid>
2. 代码实现
public partial class TestCancellationTokenThread : Window
{private CancellationTokenSource? _cts = null;private Task? _task = null;private volatile bool _isPause = false; //使用 volatile 修饰,对于当下 bool 类型的简单操作,可以替代 lock//private readonly object _lock = new object();public TestCancellationTokenThread(){InitializeComponent();}private void BtnClick_StartTask(object sender, RoutedEventArgs e){if ((_cts != null && !_cts.Token.IsCancellationRequested) || (_task != null && !_task.IsCompleted)){Debug.WriteLine("任务正在运行...");return;}_cts = new();_isPause = false; //启动任务后,自动开始Debug.WriteLine("任务开始运行...");//如果用:new Task() 构造函数方式,外层的 Task 会立刻完成,不会等待里层的 async 方法//而使用 Task.Run() + async lambda, 才是正确嵌套 Task。Task.WaitAsync 完成则表示内部真正完成。_task = Task.Run(async () => {int num = 0;var token = _cts.Token; //使用本地 token,避免循环中重复访问 _ctswhile (!token.IsCancellationRequested){if (!_isPause){Debug.WriteLine(num);num++;}try{await Task.Delay(1000, token); //取消时会立即抛出异常}catch (OperationCanceledException){break; //取消时立刻退出}}}, _cts.Token);}private async void BtnClick_StopTask(object sender, RoutedEventArgs e){if (_cts == null) return;if (!_cts.IsCancellationRequested){_cts.Cancel();}try{if (_task != null){await _task.WaitAsync(TimeSpan.FromSeconds(3));}}catch (TimeoutException){Debug.WriteLine("任务停止超时...");}finally{_cts.Dispose();_cts = null;_task = null;}Debug.WriteLine("任务已停止");}private void BtnClick_PauseTask(object sender, RoutedEventArgs e){_isPause = true;}private void BtnClick_ResumTask(object sender, RoutedEventArgs e){_isPause = false;}}
3. 运行界面