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

在.NET中不传递CancellationTokenSource如何取消无取消支持的任务?

实现方案说明

可行性结论

不修改LongTask.Run业务逻辑的前提下,无法真正终止后台的CPU密集型计算任务(因为它不响应取消令牌),但可以实现UI层面的取消交互:用户触发取消后,UI不再等待任务结果,直接更新为“已取消”状态,同时恢复操作按钮的可用性。后台的计算任务会继续执行完毕,但不会再影响UI。

具体实现步骤(MAUI + .NET 7)

核心思路是用Task.Run将同步业务逻辑包装为后台任务,通过CancellationTokenSource控制等待过程,结合Task.WaitAsync实现“取消等待”的效果,同时通过MAUI的MainThread确保UI更新的线程安全。

1. 页面布局(XAML)

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiLongTaskDemo.MainPage">

    <VerticalStackLayout Padding="20" Spacing="10">
        <Label x:Name="ResultLabel" Text="等待任务开始..." />
        <Button x:Name="StartButton" Text="开始任务" Clicked="StartButton_Clicked" />
        <Button x:Name="CancelButton" Text="取消任务" Clicked="CancelButton_Clicked" IsEnabled="False" />
    </VerticalStackLayout>

</ContentPage>

2. 后台逻辑(C#代码)

using System.Threading;

namespace MauiLongTaskDemo;

public partial class MainPage : ContentPage
{
    private CancellationTokenSource? _cts;
    private readonly LongTask _longTask = new();

    public MainPage()
    {
        InitializeComponent();
    }

    private async void StartButton_Clicked(object sender, EventArgs e)
    {
        _cts = new CancellationTokenSource();
        StartButton.IsEnabled = false;
        CancelButton.IsEnabled = true;
        ResultLabel.Text = "任务运行中...";

        try
        {
            // 将同步业务逻辑包装为后台任务
            var computeTask = Task.Run(() => _longTask.Run());
            // 等待任务完成,或响应取消信号
            var result = await computeTask.WaitAsync(_cts.Token);
            
            // 任务正常完成,更新UI
            await MainThread.InvokeOnMainThreadAsync(() =>
            {
                ResultLabel.Text = "任务完成!";
            });
        }
        catch (OperationCanceledException)
        {
            // 捕获取消异常,更新UI为取消状态
            await MainThread.InvokeOnMainThreadAsync(() =>
            {
                ResultLabel.Text = "任务已取消";
            });
        }
        catch (Exception ex)
        {
            // 处理其他异常
            await MainThread.InvokeOnMainThreadAsync(() =>
            {
                ResultLabel.Text = $"任务出错:{ex.Message}";
            });
        }
        finally
        {
            // 清理资源,恢复按钮状态
            _cts?.Dispose();
            _cts = null;
            StartButton.IsEnabled = true;
            CancelButton.IsEnabled = false;
        }
    }

    private void CancelButton_Clicked(object sender, EventArgs e)
    {
        // 触发取消信号
        _cts?.Cancel();
    }
}

// 原业务逻辑,完全不修改
public class Result
{
}

public class LongTask
{
    public Result Run()
    {
        Thread.Sleep(10000); // 模拟长时间CPU密集计算
        return new Result();
    }
}

关键细节说明

  • 取消的本质:WaitAsync(_cts.Token)只是终止了UI线程对后台任务的等待,后台的计算任务会继续执行直到完成,只是结果会被丢弃。如果需要彻底终止计算,必须修改LongTask.Run,让它接受CancellationToken并定期检查IsCancellationRequested。
  • 线程安全:MAUI的UI控件只能在主线程更新,所以必须用MainThread.InvokeOnMainThreadAsync包裹UI更新代码。
  • 资源清理:CancellationTokenSource必须在使用后Dispose,避免内存泄漏,这里在finally块中处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 09:55:34