C# 如何优雅地取消正在运行的任务(续)

在上一篇C# 如何优雅地取消正在运行的任务 虽然取消了未结束的异步任务,但是被调用的同步方法仍未结束;代码不够优雅,所以在查看了B站博主的这个C#如何在异步任务中调用及取消一个长时间运行的同步方法(其一)视频后,发现他的做法比较优雅;

注意:这个👆视频介绍的方法适用于.NET Framework平台,在.NET CORE平台下已经不再适用,如果你是.NET CORE平台 请看C#如何在异步任务中调用及取消一个长时间运行的同步方法(其二)

由于我使用的是.NET Framework平台,所以下面的方法可以使用,👉这个是博主给出的gits链接CancelableProcessTask.cs,稍稍改写了连接中给出的代码,便有了下面的方法

public static class CancelableThreadTask
{
    public static Task RunAsync(this Action action, CancellationToken token, Action onCompletedSuccessfully = null)
    {
        // 轻量级Task对象制造器
        var tcs = new TaskCompletionSource<string>();
        // 新开一个线程去执行同步方法
        var thread = new Thread(() =>
        {
            try
            {
                action();
                // 同步方法执行完成 通知外部任务结束
                tcs.SetResult("Success");
                // 执行任务结束回调方法 为什么方法定义时要传递这个入参?
                // 想想:如果调用方直接调用RunAsync()而不是使用awit RunAsync(),那他万一又关心方法什么时候执行完成呢?这个回调是不是就很有必要。
                onCompletedSuccessfully?.Invoke();
            }
            catch (Exception ex)
            {
                if (ex is ThreadAbortException)
                {
                    tcs.TrySetCanceled();
                }
                else
                {
                    tcs.TrySetException(ex);
                }
            }
        });
        // 取消令牌的Register可以帮我们监视是否有取消操作发生
        token.Register(() =>
        {
            // 若有取消操作发生 则干掉执行同步方法(即:那个入参action)所在线程
            thread.Abort();
            // _thread.Join();
            // 轻量级Task对象生成器也调用一个取消方法,告诉外层等待方:你等待的任务已经取消
            tcs.TrySetCanceled();
        });

        thread.Start();
        // 对外承诺生成一个任务
        return tcs.Task;
    }
}

使用示例:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using CancelableDemo;
using ThirdPartyLib;

namespace CancelableApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private int orderNo = 1;

        private CancellationTokenSource cts = new CancellationTokenSource();

        private async void RunBtn_OnClick(object sender, RoutedEventArgs e)
        {
            // 每次执行任务前 都尝试取消一下之前的任务
            try
            {
                cts.Cancel();
            }
            finally
            {
                cts.Dispose();
                cts = new CancellationTokenSource();
            }
            
            try
            {
                await new Action(() =>
                {
                    var o = new ThirdPartyClass();
                    // throw new Exception("--00");
                    var resp = o.LongRunningJob($"{DateTime.Now:yyyy/MM/dd HH:mm:ss}");
                }).RunAsync( cts.Token,OnComplete);
            }
            catch (TaskCanceledException ee)
            {
                Console.WriteLine("取消");
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
        }

        private void OnComplete()
        {
            Console.WriteLine("执行结束");
        }
    }
}

运行效果:
取消正在执行中的同步方法

总结,当三方API中只给出了同步接口,同时这个接口还非常耗时的情况下,

  • 如果想实现在第二次调用时,结束掉上次还未返回的任务,那么C# 如何优雅地取消正在运行的任务 这个就够了;
  • 如果不仅想结束掉未返回的任务,还希望将三方API中的同步方法也结束掉,则需要使用到Thread操作,在判断到取消操作后,结束掉响应的thread就ok了。
posted @ 2025-06-07 21:11  BigBosscyb  阅读(57)  评论(0)    收藏  举报