CancellationToken.ThrowIfCancellationRequested正确使用方法咨询
正确使用CancellationToken.ThrowIfCancellationRequested的WinForm实现方案
针对你遇到的两个问题,下面给出具体的代码实现和原理说明:
一、解决UI阻塞与Task.Run取消异常问题
之前用Task.Run启动任务后调用task.Wait()会直接阻塞UI线程,导致无法点击取消按钮,这是因为Wait()是同步等待。正确的做法是使用async/await异步等待任务,让UI线程能继续响应用户操作。
另外,Task.Run接收的令牌仅用于任务尚未开始执行时的取消,如果任务已经启动运行,必须在任务内部主动检查令牌状态,ThrowIfCancellationRequested()就是干这个的——它会在令牌已取消时抛出OperationCanceledException,这个异常是.NET异步框架认可的“正常取消”信号,await时会自动捕获,不需要额外手动判断IsCancellationRequested。
核心UI事件代码
// Form类中声明CancellationTokenSource字段 private CancellationTokenSource _cts; // 启动按钮点击事件(用async void,仅限UI事件) private async void btnStart_Click(object sender, EventArgs e) { btnStart.Enabled = false; btnCancel.Enabled = true; _cts = new CancellationTokenSource(); try { // 异步等待任务,不会阻塞UI await ExecuteLongRunningTask(_cts.Token); MessageBox.Show("操作完成"); } catch (OperationCanceledException) { MessageBox.Show("操作已取消"); } finally { btnStart.Enabled = true; btnCancel.Enabled = false; _cts?.Dispose(); _cts = null; } } // 取消按钮点击事件 private void btnCancel_Click(object sender, EventArgs e) { _cts?.Cancel(); }
二、让耗时方法响应取消请求
DoSomethingThatTakesALongTime无法获取令牌的问题,本质是需要把令牌传递到方法内部,在耗时操作的关键节点调用ThrowIfCancellationRequested()——不需要每一行都加,只要在不会长时间卡住的位置插入检查点,确保取消请求能被及时响应即可。
改造后的异步任务与耗时方法
private async Task ExecuteLongRunningTask(CancellationToken token) { // 模拟多步骤任务,每个步骤前检查取消 for (int step = 0; step < 10; step++) { // 进入耗时操作前检查取消 token.ThrowIfCancellationRequested(); // 将令牌传递给耗时方法 await DoSomethingThatTakesALongTime(step, token); } } private async Task DoSomethingThatTakesALongTime(int step, CancellationToken token) { // 模拟循环型耗时操作(比如处理大数据) for (int i = 0; i < 10000; i++) { // 每100次循环检查一次取消,平衡响应速度与性能 if (i % 100 == 0) { token.ThrowIfCancellationRequested(); } // 模拟实际耗时操作(异步) await Task.Delay(1); } // 如果是同步耗时方法,同样在关键节点检查 // for (int i = 0; i < 1000000; i++) // { // if (i % 10000 == 0) token.ThrowIfCancellationRequested(); // // 同步计算逻辑 // } }
特殊情况:无法修改的第三方耗时方法
如果DoSomethingThatTakesALongTime是第三方提供的、无法修改的阻塞方法,可以在调用它的循环中插入检查点,同时用Task.Run包装以避免阻塞UI:
private async Task ExecuteLongRunningTask(CancellationToken token) { for (int step = 0; step < 10; step++) { token.ThrowIfCancellationRequested(); // 用Task.Run包装第三方阻塞方法,并传递令牌 await Task.Run(() => ThirdPartyLongRunningMethod(step), token); } }
内容的提问来源于stack exchange,提问作者komodosp
相关产品推荐
相关产品推荐

