C#中多线程使用CancellationTokenSource进行线程管理
1. XML 代码
<Grid Margin="15" HorizontalAlignment="Left"> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="100" /> <ColumnDefinition Width="10" /> <ColumnDefinition Width="120" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="1*" /> </Grid.RowDefiniphptions> <!-- 添加任务 --> <Label Content="Task Number" Style="{StaticResource LabelStyle}" /> <TextBox x:Name="xTxtTaskNum" Grid.Column="1" Style="{StaticResource TextBoxStyle}" /> <Button Grid.Column="3" Click="BtnClick_CreateTasks" Content="创建任务" Style="{StaticResource BtnStyle}" /> <!-- 开始任务 --> <Label Grid.Row="1" Content="Task Index" Style="{StaticResource LabelStyle}" /> <TextBox x:Name="xTxtStartIdx" Grid.Row="1" Grid.Column="1" Style="{StaticResource TextBoxStyle}" /> <Button Grid.Row="1" Grid.Column="3" Click="BtnClick_StartTask" Content="开始该任务" Style="{StaticResource BtnStyle}" /> &编程lt;!-- 结束任务 --> <Label Grid.Row="2" Content="Task Index" Style="{StaticResource LabelStyle}" /> <TextBox x:Name="xTxtStopIdx" Grid.Row="2" Grid.Column="1" Style="{StaticResource TextBoxStyle}" /> <Button Grid.Row="2" Grid.Column="3" Click="BtnClick_StopTask" Content="停止该任务" Style="{StaticResource BtnStyle}" /> <!-- javascript暂停任务 --> <Label Grid.Row="3" Content="Task Index" Style="{StaticResource LabelStyle}" /> <TextBox x:Name="xTxtPauseIdx" Grid.Row="3" Grid.Column="1" Style="{StaticResource TextBoxStyle}" /> <Button Grid.Row="3" Grid.Column="3" Click="BtnClick_PauseTask" Content="暂停该任务" Style="{StaticResource BtnStyle}" /> <!-- 恢复任务 --> <Label Grid.Row="4" Content="Task Index" Style="{StaticResource LabelStyle}" /> <TextBox x:Name="xTxtResumeIdx" Grid.Row="4" Grid.Column="1" Style="{StaticResource TextBoxStyle}" /> <Button Grid.Row="4" Grid.Column="3" Click="BtnClick_ResumTask" Content="恢复该任务" Style="{StaticResource BtnStyle}" /> </Grid>
2. 代码实现
public partial class TestCancellationTokenThreads : Window { private ConcurrentDictionary<int, TaskInfo> _dictTaskInfo = new(); private int _taskNum = 0; public TestCancellationTokenThreads() { InitializeComponent(); } private void BtnClick_CreateTasks(object sender, RoutedEventArgs e) { _taskNum = Convert.ToInt32(xTxtTaskNum.Text); for (int i = 0; i < _taskNum; i++) { TaskInfo taskInfo = new() { Name = $"Task{i + 1}", Task = null, Cts = null, IsPause = false, }; _dictTaskInfo.TryAdd(i, taskInfo); } Debug.WriteLine($"{_taskNum}个任务创建成功!"); } private void BtnClick_StartTask(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(xTxtStartIdx.Text)) return; int idx = Convert.ToInt32(xTxtStartIdx.Text); if (idx < 0 || idx >= _taskNum) { Debug.WriteLine($"任务序号超出任务总数{_taskNum}"); return; } TaskInfo taskInfo = _dictTaskInfo[idx]; if ((taskInfo.Cts != null && !taskInfo.Cts.IsCancellationRequested) || (taskInfo.Task != null && !taskInfo.Task.IsCompleted)) { Debug.WriteLine($"任务: {taskInfo.Name}正在运行..."); return; } taskInfo.Cts = new(); taskInfo.IsPause = false; //启动任务后,自动开始 Debug.WriteLine("任务开始运行..."); CancellationToken token = taskInfo.Cts.Token; //创建任务 taskInfo.Task = Task.Run(async () => { int numIdx = 0; //执行任务的,用 Token 的 IsCancellationRequested while (!token.IsCancellationRequested) { if (!taskInfo.IsPause) { Debug.WriteLine(numIdx); numIdx++; } try { await Task.Delay(1000, token); php } catch (OperationCanceledException) { break; //取消则立刻退出 } } }, token); } private async void BtnClick_StopTask(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(xTxtStopIdx.Text)) return; int idx = Convert.ToInt32(xTxtStopIdx.Text); if (idx < 0 || idx >= _taskNum) { Debug.WriteLine($"任务序号超出任务总数{_taskNum}"); return; } TaskInfo taskInfo = _dictTaskInfo[idx]; if (taskInfo.Cts == null || taskInfo.Task == null) return; //触发取消的操作,用 Cts 的 IsCancellationRequested if (!taskInfo.Cts.IsCancellationRequested) { taskInfo.Cts.Cancel(); } try { await taskInfo.Task.WaitAsync(TimeSpan.FromMilliseconds(3000)); } catch (TimeoutException) { 编程客栈 Debug.WriteLine($"任务:{taskInfo.Name} 停止超时!"); } finally { taskInfo.Cts.Dispose(); taskInfo.Cts = null; taskInfo.Task = null; } Debug.WriteLine($"任务:{taskInfo.Name} 已停止"); } private void BtnClick_PauseTask(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(xTxtPauseIdx.Text)) return; int idx = Convert.ToInt32(xTxtPauseIdx.Text); if (idx < 0 || idx >= _taskNum) { Debug.WriteLine($"任务序号超出任务总数{_taskNum}"); return; } TaskInfo taskInfo = _dictTaskInfo[idx]; taskInfo.IsPause = true; } private void BtnClick_ResumTask(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(xTxtResumeIdx.Text)) return; int idx = Convert.ToInt32(xTxtResumeIdx.Text); if (idx < 0 || idx >= _taskNum) { Debug.WriteLine($"任务序号超出任务总数{_taskNum}"); return; } TaskInfo taskInfo = _dictTaskInfo[idx]; taskInfo.IsPause = false; } } public class TaskInfo { public string? Name { get; set; } public Task? Task { get; set; } public CancellationTokenSource? Cts { get; set; } public bool IsPause { get => _isPause; set => _isPause = value; } public object Locker { get; set; } = new(); //以这样的方式,保留 volatile 的功能性 //volatile: 适用于一个线程读,一个线程写的情况,并且是简单类型的简单操作 (不能用于 struct等、IsPause = !IsPause 这种 "修改+写入"),并且只能字段 private volatile bool _isPause; }
3. 运行
到此这篇关于C#中多线程使用CancellationTokenSource进行线程管理的文章就介绍到这了,更多相关C# 线程管理内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论