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

在实际应用中,异步线程可能需要等待另外的任务完成情况来确定本任务的完成状态。例如图像分析的结果,依赖于图像分析算法对图像的分析,在这种情况下,图像分析并非一种返回结果,根据分析返回的结果来确认图像是好的,还是坏的,进而手动设置异步线程的结果。今天我们以一个简单的小例子,简述在.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");
	});
}

示例演示

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

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

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

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

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

相关推荐
CSharp精选营11 小时前
.NET 8 与 .NET 9 支持终止倒计时:开发者需要了解什么
.net·lts·.net8·.net9·码农刚子·升级迁移·sts·支持终止
hez20103 天前
在 .NET 上构建超大托管数组
c#·.net·.net core·gc·clr
雨落倾城夏未凉8 天前
第四章c#方法-参数数组和可选参数(16)
后端·c#
唐青枫9 天前
线程不是越多越快:C#.NET Thread 生命周期、同步与后台工作线程实战
c#·.net
唐青枫10 天前
别只会反射:C#.NET Emit 动态生成代码实战详解
c#·.net
Caco_D10 天前
一行代码抓遍全网 20 个热榜!Aneiang.Pa 4.0 发布 — 极简 .NET 爬虫库
爬虫·.net
咕白m62510 天前
.NET 环境下 Word 超链接批量提取方案
c#·.net
用户917215619021110 天前
C# 通信协议增量解析:用状态机处理半包和粘包
c#
小码编匠11 天前
C# 工控上位机必备:数据转换工具类与十个核心模块
后端·c#·.net