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

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

示例演示

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

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

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

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

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

相关推荐
学以智用14 小时前
.NET 10 完整详解
.net
玩泥巴的16 小时前
MAF + 飞书:让大模型干活,但别让它乱干
ai·c#·.net·飞书·agent
Uncertainty!!19 小时前
Windows 10 与 Ubuntu 22.04 通过 Samba 共享文件夹
windows·ubuntu·共享文件夹
cpp_w20 小时前
C#基础3- 面向对象中级应用3
后端·c#
步步为营DotNet21 小时前
Async 状态机要搬进运行时了:.NET 10 Runtime Async 实测,快的地方快 1.5 倍,贵的地方贵 5 倍
性能优化·c#·.net·async·runtime
百事牛科技21 小时前
如何创建7Z压缩包?Windows下7-Zip操作方法
windows·7-zip
hixiong12321 小时前
C# 使用 ZXingCpp.Angle 进行条码、二维码识别
开发语言·c#·扫码
步步为营DotNet21 小时前
System.Text.Json 源生成:三个“看起来已经生效“的静默失效
c#·.net·native aot·源生成
池央1 天前
Windows本地部署Qwen3.8实战:LM Studio、Cherry Studio、远程API与Token鉴权
windows