You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Task.Run创建重复任务时,如何取消旧任务并确保单任务运行?

问题:创建新任务时取消旧的长期运行任务

我写了如下代码,用Task.Run执行一个长期运行的while循环:

public void Test(){
    var task1 = Task.Run(() =>
    {
        while(true){
            // 无限循环
        }
    }
}

调用Test()时,task1会执行,要是立刻再次调用Test(),就会同时存在两个任务。请问有没有办法在创建新任务时取消已在运行的旧任务?


参考Michał Turczyn的回复后,我修改了代码:

int i = 0;
private CancellationTokenSource cancellationTokenSource;

public void Test(int b)
{
    if (cancellationTokenSource != null)
    {
        // 取消之前创建的任务
        cancellationTokenSource.Cancel();
    }

    cancellationTokenSource = new CancellationTokenSource();

    var task1 = Task.Run(() =>
    {
        while (!cancellationTokenSource.IsCancellationRequested)
        {
            Console.WriteLine(b);
            // 无限循环
        }
    });
}

public void button_pushed(){
    Console.WriteLine("pushed");
    Test(i); i++; return;
}

日志输出如下:

pushed
0
0
0
0
0
pushed
1
1
1
1
pushed
1
1
1
1
2
2
2
2
2
1
1
1
1
2
2
2
pushed
1
1
1
1
2
2
2
2
2
1
1
1
1
3
3
3
3
2
2
2

....

看起来旧任务还在运行,但第三次执行后,最初的输出(0)消失了,这是时序问题吗?我期望的输出效果是:

pushed
0
0
0
0
pushed
1
1
1
1
pushed
2
2
2
2

....

问题根源

  1. 闭包导致取消信号失效:旧任务捕获的是cancellationTokenSource变量的引用,当你创建新任务时把这个变量指向了新的实例,旧任务里的取消判断实际上在检查新令牌的状态,根本没收到原本的取消信号,所以会继续运行。
  2. 未等待旧任务终止:调用Cancel()后立刻启动新任务,旧任务不会马上停止,就算取消信号正常,它也得等到下一次循环检查时才会退出,更别说现在信号还失效了。

修复方案

给每个任务绑定独立的取消令牌,同时等待旧任务完全终止后再启动新任务:

int i = 0;
private CancellationTokenSource _previousCts;
private Task _runningTask;

public async void Test(int b)
{
    // 处理旧任务:取消+等待终止+释放资源
    if (_previousCts != null)
    {
        _previousCts.Cancel();
        try
        {
            if (_runningTask != null)
                await _runningTask;
        }
        catch (OperationCanceledException)
        {
            // 任务被取消属于预期情况,无需额外处理
        }
        finally
        {
            _previousCts.Dispose();
            _previousCts = null;
        }
    }

    // 创建新的取消令牌和任务
    _previousCts = new CancellationTokenSource();
    var token = _previousCts.Token;

    _runningTask = Task.Run(() =>
    {
        while (!token.IsCancellationRequested)
        {
            Console.WriteLine(b);
            // 可选:加短延迟,既避免输出过于密集,也让取消检查更及时
            // Thread.Sleep(100);
        }
    }, token);
}

public void button_pushed()
{
    Console.WriteLine("pushed");
    Test(i);
    i++;
}

修复说明

  1. 绑定独立令牌:每个任务捕获的是当前的token实例,后续修改_previousCts不会影响旧任务的取消判断,确保旧任务能收到正确的取消信号。
  2. 等待旧任务终止:用await _runningTask确保旧任务完全退出后再启动新任务,彻底避免多任务同时输出的情况。
  3. 资源释放:调用Dispose()释放旧的CancellationTokenSource,避免资源泄漏。

修改后每次点击按钮,旧任务会被正确取消并终止,新任务才会启动,输出就能符合你的预期。

内容的提问来源于stack exchange,提问作者yolo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 07:01:00