C

一赫技术 2024-04-10 10:39:03

在C#中,Task和async/await是处理异步操作的强大工具。然而,在某些情况下,我们可能需要更细粒度地控制任务的完成状态,而不是简单地等待它完成。这时,TaskCompletionSource<T>就派上了用场。TaskCompletionSource<T>允许我们手动地设置任务的结果、异常或取消状态,从而提供了对任务执行的更高级别控制。本文将介绍TaskCompletionSource<T>的基本用法,并通过一个WinForms示例展示如何使用它来控制任务结果。

TaskCompletionSource<T>简介

TaskCompletionSource<T>是一个非常有用的类,它允许创建一个没有绑定到某个特定操作的Task<T>。这意味着你可以手动控制这个Task<T>的完成时机和结果。这在你需要将基于事件的异步模式与基于任务的异步模式进行桥接时非常有用。

示例1:处理超时

在异步操作中处理超时是一个常见的需求。使用TaskCompletionSource<T>,我们可以轻松实现这一功能。

using System;using System.Threading.Tasks;using System.Windows.Forms;namespace TaskCompletionSourceDemo{ public partial MainForm : Form { public MainForm() { InitializeComponent(); } private async void btnStartOperationWithTimeout_Click(object sender, EventArgs e) { var tcs = new TaskCompletionSource<bool>(); var timeout = Task.Delay(5000); // 5秒超时 // 模拟异步操作 Task.Run(async () => { await Task.Delay(3000); // 模拟3秒的操作 tcs.TrySetResult(true); // 尝试设置结果 }); if (await Task.WhenAny(tcs.Task, timeout) == timeout) { // 超时逻辑 MessageBox.Show("操作超时!"); } else { // 操作完成 bool result = await tcs.Task; lblResult.Text = result ? "操作成功完成" : "操作失败"; } } }}

在这个示例中,我们创建了一个超时任务timeout,并使用Task.WhenAny等待操作任务和超时任务中的任何一个完成。如果超时任务先完成,我们就认为操作超时了。

示例2:处理取消操作

使用TaskCompletionSource<T>还可以方便地处理用户取消操作的情况。

using System;using System.Threading;using System.Threading.Tasks;using System.Windows.Forms;namespace TaskCompletionSourceDemo{ public partial MainForm : Form { private CancellationTokenSource cts; public MainForm() { InitializeComponent(); } private async void btnStartCancelableOperation_Click(object sender, EventArgs e) { cts = new CancellationTokenSource(); var tcs = new TaskCompletionSource<bool>(); // 监听取消事件 cts.Token.Register(() => tcs.TrySetCanceled()); // 模拟异步操作 Task.Run(async () => { try { await Task.Delay(3000, cts.Token); // 模拟3秒的操作,支持取消 tcs.TrySetResult(true); // 尝试设置结果 } catch (TaskCanceledException) { // 操作被取消 tcs.TrySetCanceled(); } }); try { await tcs.Task; lblResult.Text = "操作成功完成"; } catch (TaskCanceledException) { lblResult.Text = "操作被取消"; } } private void btnCancelOperation_Click(object sender, EventArgs e) { cts?.Cancel(); } }}

在这个示例中,我们创建了一个CancellationTokenSource和对应的CancellationToken,并在用户请求取消时调用Cancel方法。我们还通过Register方法注册了一个取消时的回调,以便在取消发生时尝试设置任务的取消状态。

示例3:桥接基于事件的异步模式

TaskCompletionSource<T>也可以用于将基于事件的异步模式转换为基于任务的异步模式。

public partial Form1 : Form{ public Form1() { InitializeComponent(); } private async void bntStart_Click(object sender, EventArgs e) { var tcs = new TaskCompletionSource<bool>(); // 事件处理程序 EventHandler<OperationCompletedEventArgs> handler = null; // 初始化为null,以便在lambda表达式中引用 handler = (sender, e) => { // 取消事件订阅,防止事件再次触发 ((SomeEventBasedAsyncOperation)sender).OperationCompleted -= handler; if (e.Success) tcs.TrySetResult(true); // 尝试设置任务结果为成功 else tcs.TrySetException(new Exception("操作失败")); // 尝试设置任务结果为异常 }; // 开始异步操作 SomeEventBasedAsyncOperation operation = new SomeEventBasedAsyncOperation(); operation.OperationCompleted += handler; // 订阅事件 operation.Start(); try { // 等待任务完成 bool result = await tcs.Task; lblResult.Text = result ? "操作成功完成" : "操作失败"; } catch (Exception ex) { // 处理任务异常 lblResult.Text = $"操作出错:{ex.Message}"; } }}public SomeEventBasedAsyncOperation{ public event EventHandler<OperationCompletedEventArgs> OperationCompleted; public void Start() { // 模拟异步操作的完成 Task.Delay(3000).ContinueWith(t => { OperationCompleted?.Invoke(this, new OperationCompletedEventArgs { Success = true }); }); }}public OperationCompletedEventArgs : EventArgs{ public bool Success { get; set; }}

事件处理程序注销:通过在事件处理程序内部使用-= handler来注销事件处理程序,避免在设置TaskCompletionSource的结果后,事件处理程序被再次触发。使用**TrySetResult和TrySetException**:这些方法在尝试设置结果或异常时,如果TaskCompletionSource的任务已经完成(例如,已经设置了结果或异常),它们不会抛出异常。这提供了额外的安全性,防止因重复设置结果而导致的错误。异常处理:在等待任务完成时,使用try-catch块来捕获和处理可能的异常,这样即使操作失败或发生异常,应用程序也能以一种用户友好的方式处理这些情况。总结

TaskCompletionSource<T>是C#中处理异步操作的一个强大工具,它允许开发者在任何时候手动地控制任务的状态。通过本文的WinForms示例,我们展示了如何使用TaskCompletionSource<T>来控制长时间运行操作的结果,并在操作完成时更新UI。这种方法为处理复杂的异步场景提供了更多的灵活性和控制能力。

0 阅读:0

一赫技术

简介:感谢大家的关注