推荐一种手动设置异步线程等待机制的解决方案

在实际应用中,异步线程可能需要等待另外的任务完成情况来确定本任务的完成状态。例如图像分析的结果,依赖于图像分析算法对图像的分析,在这种情况下,图像分析并非一种返回结果,根据分析返回的结果来确认图像是好的,还是坏的,进而手动设置异步线程的结果。今天我们以一个简单的小例子,简述在.NET开发中,如何通过TaskCompletionSource来手动设置异步线程的等待结果,仅供学习分享使用,如有不足之处,还请指正。

TaskCompletionSource概述

TaskCompletionSource<TResult> 是 .NET 中用于手动控制 Task<TResult> 完成时机的工具,常用于将事件驱动或回调模式转换为 async/await 友好形式。TaskCompletionSource有一个属性Task,用于获取当前需要等待的任务。通过TaskCompletionSource,可以设置三种返回结果,如下所示:

  • SetCanceled:将TaskCompletionSource.Task 转换为Cancel状态,并在原等待任务中抛出TaskCanceledException异常。
  • SetException:将TaskCompletionSource.Task 转换为Faluted状态,并在原等待任务中抛出对应的Exception异常。
  • SetResult:将TaskCompletionSource.Task 转换为RanToCompletion状态,并设置TResult类型的结果。
  • TrySetCanceled:尝试将TaskCompletionSource.Task 转换为Canceled状态,,并在原等待任务中抛出TaskCanceledException异常。
  • TrySetException:尝试将TaskCompletionSource.Task 转换为Faluted状态,并在原等待任务中抛出对应的Exception异常。
  • TrySetResult:将TaskCompletionSource.Task 转换为RanToCompletion状态,并设置TResult类型的结果。

对于上述设置方法,在调用时有以下两点需要注意:

  • 对于SetCanceled,SetException,SetResult方法,如果当前Task状态已经是RanToCompletion,Faulted,Canceled,则在设置时抛出InvalidOperationException。
  • 对于TrySetCanceled,TrySetException,TrySetResult,如果Task已经是RanToCompletion,Faulted,Canceled,则返回false,否则返回true。

在上述TaskCompletionSource设置返回结果的方法中,涉及到当前Task对应的状态,对于一个Task类型的任务,主要有以下几种:

  • Created‌:任务已创建但尚未启动。
  • ‌WaitingForActivation‌:任务已创建但未被激活(例如通过Start方法)。
  • ‌WaitingToRun‌:任务已激活但尚未开始执行。
  • ‌Running‌:任务正在执行中。
  • ‌WaitingForChildrenToComplete‌:任务本身已完成,但其子任务仍在运行(仅适用于父任务)。
  • ‌RanToCompletion‌:任务已成功完成。
  • ‌Canceled‌:任务因取消操作终止。
  • ‌Faulted‌:任务因未处理异常终止。 ‌

Task的主要属性,如下所示:

  • ‌Task.Status‌:获取当前任务的TaskStatus值。 ‌
  • ‌IsCanceled‌:检查任务是否因取消完成。 ‌
  • ‌IsFaulted‌:检查任务是否因异常完成。
  • ‌IsCompleted‌:检查任务是否已完成(无论成功、取消或异常)。 ‌

TaskCompletionSource用法

接下来,以一个简单的小例子介绍TaskCompletionSource的使用方法,首先创建的一个任务异步执行Running方法,并在Running方法中等待TaskCompletionSource.Task的完成和获取结果,如下所示:

cs 复制代码
private Task task;
 
private TaskCompletionSource<bool> tcs;
 
public MainForm()
{
    InitializeComponent();
}
 
private void btnStart_Click(object sender, EventArgs e)
{
    tcs = new TaskCompletionSource<bool>();
    task = Task.Run(async () =>
    {
        await Running(this.tcs);
    });
 
}
 
private async Task Running(TaskCompletionSource<bool> tcs)
{
    try
    {
        this.OutputInfo("当前开始工作中");
        bool result = await tcs.Task;
        this.OutputInfo($"当前已完成工作,IsComplete={tcs.Task.IsCompleted},结果为{result}");
    }
    catch (TaskCanceledException ex)
    {
        this.OutputInfo($"当前已取消工作,IsCancelled = {tcs.Task.IsCanceled}");
        this.OutputInfo($"异常信息如下:");
        this.OutputInfo($"{ex.Message}");
    }
    catch (Exception ex2)
    {
        this.OutputInfo($"当前工作出现了异常,IsFaulted={tcs.Task.IsFaulted}");
        this.OutputInfo($"异常信息如下:");
        this.OutputInfo($"{ex2.Message}");
    }
}

设置Result

当任务正常完成时,通过调用SetResult或TrySetResult方法,将任务状态转换为RanToCompletion,Running方法中的任务正常完成。此方法接收一个参数,用于设置Task返回的结果,类型由定义TaskCompletionSource时指定的泛型确定,如下所示:

cs 复制代码
private void btnEnd_Click(object sender, EventArgs e)
{
	if (this.tcs != null && this.tcs.Task.Status==TaskStatus.WaitingForActivation)
	{
		this.tcs.SetResult(true);
	}
}

设置Canceled

当任务被取消时,通过调用SetCanceled或TrySetCanceled方法,将任务状态转换为Canceled,此时Running方法中的任务将抛出TaskCanceledException,如下所示:

cs 复制代码
private void btnCancel_Click(object sender, EventArgs e)
{
	if (this.tcs != null && this.tcs.Task.Status == TaskStatus.WaitingForActivation)
	{
		this.tcs.SetCanceled();
	}
}

设置Exception

当任务执行过程中出现异常时,通过调用SetException或TrySetException方法,将任务状态转换为Faluted,此时Running方法总的任务将抛出对应设置的Exception,如下所示:

cs 复制代码
private void btnExce_Click(object sender, EventArgs e)
{
	if (this.tcs != null && this.tcs.Task.Status == TaskStatus.WaitingForActivation)
	{
		this.tcs.SetException(new Exception("Okcoder异常出现"));
	}
}

在上述方法中,引用的Output方法为自定义方法,用于将信息输出到文本框中,如下所示:

cs 复制代码
private void OutputInfo(string msg)
{
	this.Invoke(() =>
	{
		this.txtInfo.AppendText($"{msg}\r\n");
	});
}

示例演示

在本示例中,首先有一个开始按钮,用于启动任务:

结束按钮,用于设置正常的任务结束,并返回结果

一个取消按钮,用于取消任务

一个异常按钮,用于设置运行过程中出现的异常信息

以上就是《推荐一种手动设置异步线程等待机制的解决方案》的全部内容,旨在抛砖引玉,一起学习,共同进步!

相关推荐
ou.cs5 小时前
WPF TreeView 自动展开所有节点:附加行为(Attached Behavior)保姆级实现教程
c#·.net·wpf
鱼蛋-Felix5 小时前
C#浮点数在部分国家解析失效问题
开发语言·unity·c#
用户298698530145 小时前
C# Word文档页面操作:告别手动,高效掌控你的Word文档!
后端·c#·.net
悾说6 小时前
xRDP实现Linux图形化通过Windows RDP访问Linux远程桌面
linux·运维·windows
LeetCode天天刷6 小时前
1348 推文计数【区间】
java·服务器·windows
flysh056 小时前
委托实战案例
开发语言·c#
一念春风6 小时前
可视化视频编辑(WPF C#)
开发语言·c#·wpf
wregjru6 小时前
【C++】2.8C++11特性
windows
鸠摩智首席音效师7 小时前
如何查看 Windows 上安装的 .NET Framework 版本 ?
windows·.net
步步为营DotNet7 小时前
深度解读.NET中ConcurrentDictionary:高效线程安全字典的原理与应用
java·安全·.net