优化异步操作:掌握.NET Framework 4的统一取消模型

作者:微信公众号:【架构师老卢】
1-26 14:27
212

概述:.NET Framework 4引入了统一的取消模型,用于异步或长时间运行的同步操作。通过取消令牌源,您可以在任务执行过程中取消操作。这一模型简单易懂:创建令牌源,传递给异步任务,并定期检查取消请求。同时,.NET提供了自动设置取消标志的方法,以及链接取消令牌源以自动取消相关任务的功能。这些机制大大简化了异步操作的管理和控制。

从 .NET Framework 4 开始,Microsoft 提供了一个统一的模型来取消异步或长时间运行的同步操作:

取消令牌源 实现取消的一般模式是:

创建 CancellationTokenSource 对象 将新创建的 CancellationTokenSource 对象的 Token 属性传递给异步任务 在异步任务中,定期检查 CancellationToken.IsCancellationRequested 属性的值。如果为 true,则表示用户已取消操作。您需要正常结束操作。 在主线程中,可以通过调用 CancellationTokenSource.Cancel() 方法取消异步任务。这会将 CancellationToken.IsCancellationRequested 属性设置为 true。 代码示例 以下代码片段演示了如何使用 CancellationTokenSource。该窗体有两个按钮:“开始”和“取消”。单击“开始”按钮将初始化一个长时间运行的任务。单击“取消”按钮将触发 CancellationTokenSource 的 Cancel() 方法调用,并将 IsCancellationRequested 属性设置为 true。长时间运行的任务每秒检查一次此值,并在其值为 true 时终止。

namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        private CancellationTokenSource ctsDemo;
        static Form1()
        {
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            ctsDemo = new CancellationTokenSource();
            try
            {
                LongRunningTask(ctsDemo.Token);
            }
            catch (Exception ex)
            {
                Log(ex.Message);
            }
        }

        private void Log(string message)
        {
            txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
        }

        private void LongRunningTask(CancellationToken token)
        {
            Cursor = Cursors.WaitCursor;
            Task.Factory.StartNew(() =>
            {
                while (!token.IsCancellationRequested)
                {
                    Log("Task running\r\n");

                    Task.Delay(TimeSpan.FromSeconds(1)).Wait();
                }
                Log("Task cancelled\r\n");
            }, token);
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            Cursor = Cursors.Default;
            ctsDemo.Cancel();
            ctsDemo.Dispose();
        }
    }
}

自动将 IsCancellationRequested 设置为 true 通过调用 CancellationTokenSource.CancelAfter(

代码示例 以下代码片段将取消时间设置为 5 秒。5 秒后,框架会将令牌的 IsCancellationRequested 属性设置为 true,因此任务将结束。

namespace WinFormsApp1
{
    public partial class Form2 : Form
    {
        private CancellationTokenSource ctsDemo;

        public Form2()
        {
            InitializeComponent();
        }

        private async void btnStart_Click(object sender, EventArgs e)
        {
            ctsDemo = new CancellationTokenSource();
            ctsDemo.CancelAfter(TimeSpan.FromSeconds(5));
            try
            {
                Cursor = Cursors.WaitCursor;
                await LongRunningTask(ctsDemo.Token);
            }
            catch (Exception ex)
            {
                Log(ex.Message);
            }
            finally { Cursor = Cursors.Default; }
        }

        private Task LongRunningTask(CancellationToken token)
        {
            return Task.Factory.StartNew(() =>
            {
                int count = 0;
                while (!token.IsCancellationRequested)
                {
                    count++;
                    Log($"Task {Task.CurrentId} running {count}\r\n");
                    Task.Delay(TimeSpan.FromSeconds(1)).Wait();
                }
                Log("Task cancelled\r\n");
            }, token);
        }

        private void Log(string message)
        {
            txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
        }
    }
}

链接取消令牌源 您可以使用 CancellationTokenSource.CreateLinkedTokenSource(params CancellationToken[] tokens) 方法创建链接的取消令牌源。当任何源令牌处于“已取消”状态时,新创建的取消令牌源将处于“已取消”状态。如果您有多个任务正在运行,这将非常有用。其中一个任务依赖于其他任务。如果任何其他任务失败或取消,则不希望可靠任务继续执行。在这种情况下,您可以使用链接的取消令牌源来自动取消可靠的任务。

代码示例 在下面的代码片段中,我们创建了一个主要的取消令牌源,并使用 CreateLinkedTokenSource() 方法创建了一个链接的取消令牌源。

当主取消令牌源被取消时,链接的源也会自动取消。但是,如果只是取消链接的令牌源,则主令牌源不受影响。

namespace WinFormsApp1
{
    public partial class Form3 : Form
    {
        private CancellationTokenSource ctsMain;
        private CancellationTokenSource ctsLinked;
        public Form3()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            ctsMain = new CancellationTokenSource();
            ctsLinked = CancellationTokenSource.CreateLinkedTokenSource(ctsMain.Token);
            try
            {
                LongRunningTask(ctsMain.Token);
                LongRunningTask(ctsLinked.Token);
            }
            catch (Exception ex)
            {
                Log(ex.Message);
            }
        }

        private void Log(string message)
        {
            txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
        }

        private Task LongRunningTask(CancellationToken token)
        {
            return Task.Factory.StartNew(() =>
            {
                while (!token.IsCancellationRequested)
                {
                    Log($"Task {Task.CurrentId} running\r\n");

                    Task.Delay(TimeSpan.FromSeconds(1)).Wait();
                }
                Log($"Task {Task.CurrentId} cancelled\r\n");
            }, token);
        }

        private void btnCancel1_Click(object sender, EventArgs e)
        {
            ctsMain.Cancel();
        }

        private void btnCancel2_Click(object sender, EventArgs e)
        {
            ctsLinked.Cancel();
        }
    }
}
相关留言评论
昵称:
邮箱:
阅读排行