九月丶 發表於 2025-12-1 22:41:00

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

<p>在实际应用中,异步线程可能需要等待另外的任务完成情况来确定本任务的完成状态。例如图像分析的结果,依赖于图像分析算法对图像的分析,在这种情况下,图像分析并非一种返回结果,根据分析返回的结果来确认图像是好的,还是坏的,进而手动设置异步线程的结果。今天我们以一个简单的小例子,简述在.NET开发中,如何通过TaskCompletionSource来手动设置异步线程的等待结果,仅供学习分享使用,如有不足之处,还请指正。</p>
<p><img src="https://img2024.cnblogs.com/blog/1068941/202512/1068941-20251201224102518-1276699081.png"></p>
<p>&nbsp;</p>
<h1>TaskCompletionSource概述</h1>
<p>&nbsp;</p>
<p>TaskCompletionSource&lt;TResult&gt; 是 .NET 中用于手动控制 Task&lt;TResult&gt; 完成时机的工具,常用于将事件驱动或回调模式转换为 async/await 友好形式。TaskCompletionSource有一个属性Task,用于获取当前需要等待的任务。通过TaskCompletionSource,可以设置三种返回结果,如下所示:</p>
<ul class="tree-group">
<li>SetCanceled:将TaskCompletionSource.Task 转换为<span class="tree-item is-leaf has-external-link-indicator">Cancel</span>状态,并在原等待任务中抛出TaskCanceledException异常。</li>
<li>SetException:将TaskCompletionSource.Task 转换为Faluted状态,并在原等待任务中抛出对应的Exception异常。</li>
<li>SetResult:将TaskCompletionSource.Task 转换为RanToCompletion状态,并设置TResult类型的结果。</li>
<li>TrySetCanceled:尝试将TaskCompletionSource.Task 转换为<span class="tree-item is-leaf has-external-link-indicator">Canceled</span>状态,,并在原等待任务中抛出TaskCanceledException异常。</li>
<li>TrySetException:尝试将TaskCompletionSource.Task 转换为Faluted状态,并在原等待任务中抛出对应的Exception异常。</li>
<li>TrySetResult:将TaskCompletionSource.Task 转换为RanToCompletion状态,并设置TResult类型的结果。</li>
</ul>
<p>对于上述设置方法,在调用时有以下两点需要注意:</p>
<ul>
<li>对于SetCanceled,SetException,SetResult方法,如果当前Task状态已经是RanToCompletion,Faulted,Canceled,则在设置时抛出InvalidOperationException。</li>
<li>对于TrySetCanceled,TrySetException,TrySetResult,如果Task已经是RanToCompletion,Faulted,Canceled,则返回false,否则返回true。</li>
</ul>
<p>在上述TaskCompletionSource设置返回结果的方法中,涉及到当前Task对应的状态,对于一个Task类型的任务,主要有以下几种:</p>
<ul>
<li>Created‌:任务已创建但尚未启动。</li>
<li>‌WaitingForActivation‌:任务已创建但未被激活(例如通过Start方法)。</li>
<li>‌WaitingToRun‌:任务已激活但尚未开始执行。</li>
<li>‌Running‌:任务正在执行中。</li>
<li>‌WaitingForChildrenToComplete‌:任务本身已完成,但其子任务仍在运行(仅适用于父任务)。</li>
<li>‌RanToCompletion‌:任务已成功完成。</li>
<li>‌Canceled‌:任务因取消操作终止。</li>
<li>‌Faulted‌:任务因未处理异常终止。 ‌</li>
</ul>
<p>Task的主要属性,如下所示:</p>
<ul>
<li>‌Task.Status‌:获取当前任务的TaskStatus值。 ‌</li>
<li>‌IsCanceled‌:检查任务是否因取消完成。 ‌</li>
<li>‌IsFaulted‌:检查任务是否因异常完成。&nbsp;</li>
<li>‌IsCompleted‌:检查任务是否已完成(无论成功、取消或异常)。 ‌</li>
</ul>
<p>&nbsp;</p>
<h1>TaskCompletionSource用法</h1>
<p>&nbsp;</p>
<p>接下来,以一个简单的小例子介绍TaskCompletionSource的使用方法,首先创建的一个任务异步执行Running方法,并在Running方法中等待TaskCompletionSource.Task的完成和获取结果,如下所示:</p>
<pre class="language-csharp highlighter-hljs"><code>private Task task;

private TaskCompletionSource&lt;bool&gt; tcs;

public MainForm()
{
    InitializeComponent();
}

private void btnStart_Click(object sender, EventArgs e)
{
    tcs = new TaskCompletionSource&lt;bool&gt;();
    task = Task.Run(async () =&gt;
    {
      await Running(this.tcs);
    });

}

private async Task Running(TaskCompletionSource&lt;bool&gt; 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}");
    }
}</code></pre>
<p>&nbsp;</p>
<h2>设置Result</h2>
<p>&nbsp;</p>
<p>当任务正常完成时,通过调用SetResult或TrySetResult方法,将任务状态转换为RanToCompletion,Running方法中的任务正常完成。此方法接收一个参数,用于设置Task返回的结果,类型由定义TaskCompletionSource时指定的泛型确定,如下所示:</p>
<pre class="language-csharp highlighter-hljs"><code>private void btnEnd_Click(object sender, EventArgs e)
{
        if (this.tcs != null &amp;&amp; this.tcs.Task.Status==TaskStatus.WaitingForActivation)
        {
                this.tcs.SetResult(true);
        }
}</code></pre>
<p>&nbsp;</p>
<h2>设置Canceled</h2>
<p>&nbsp;</p>
<p>当任务被取消时,通过调用SetCanceled或TrySetCanceled方法,将任务状态转换为<span class="tree-item is-leaf has-external-link-indicator">Canceled,此时Running方法中的任务将抛出TaskCanceledException,如下所示:</span></p>
<pre class="language-csharp highlighter-hljs"><code>private void btnCancel_Click(object sender, EventArgs e)
{
        if (this.tcs != null &amp;&amp; this.tcs.Task.Status == TaskStatus.WaitingForActivation)
        {
                this.tcs.SetCanceled();
        }
}</code></pre>
<p>&nbsp;</p>
<h2>设置Exception</h2>
<p>&nbsp;</p>
<p>当任务执行过程中出现异常时,通过调用SetException或TrySetException方法,将任务状态转换为Faluted,此时Running方法总的任务将抛出对应设置的Exception,如下所示:</p>
<pre class="language-csharp highlighter-hljs"><code>private void btnExce_Click(object sender, EventArgs e)
{
        if (this.tcs != null &amp;&amp; this.tcs.Task.Status == TaskStatus.WaitingForActivation)
        {
                this.tcs.SetException(new Exception("Okcoder异常出现"));
        }
}</code></pre>
<p>在上述方法中,引用的Output方法为自定义方法,用于将信息输出到文本框中,如下所示:</p>
<pre class="language-csharp highlighter-hljs"><code>private void OutputInfo(string msg)
{
        this.Invoke(() =&gt;
        {
                this.txtInfo.AppendText($"{msg}\r\n");
        });
}</code></pre>
<p>&nbsp;</p>
<h1>示例演示</h1>
<p>&nbsp;</p>
<p>在本示例中,首先有一个开始按钮,用于启动任务:</p>
<p><img src="https://img2024.cnblogs.com/blog/1068941/202511/1068941-20251130235629247-877098805.png"></p>
<p>结束按钮,用于设置正常的任务结束,并返回结果</p>
<p><img src="https://img2024.cnblogs.com/blog/1068941/202511/1068941-20251130235741397-1536644279.png"></p>
<p>一个取消按钮,用于取消任务</p>
<p><img src="https://img2024.cnblogs.com/blog/1068941/202511/1068941-20251130235822418-718610224.png"></p>
<p>一个异常按钮,用于设置运行过程中出现的异常信息</p>
<p><img src="https://img2024.cnblogs.com/blog/1068941/202511/1068941-20251130235930197-192257794.png"></p>
<p>以上就是《推荐一种手动设置异步线程等待机制的解决方案》的全部内容,旨在抛砖引玉,一起学习,共同进步!</p>

</div>
<div id="MySignature" role="contentinfo">
    <div id="AllanboltSignature">

    <p style="border-top: #e0e0e0 1px dashed; border-right: #e0e0e0 1px dashed; border-bottom: #e0e0e0 1px dashed; border-left: #e0e0e0 1px dashed; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 30px; font-family: 微软雅黑; font-size: 12px" id="PSignature">
<br>

   <img alt="" src="https://images.cnblogs.com/cnblogs_com/hsiang/1154298/o_115f1cd8.jpg" width="80px" height="80px">
   
    作者:老码识途
    <br>
    出处:http://www.cnblogs.com/hsiang/
    <br>
    本文版权归作者和博客园共有,写文不易,支持原创,欢迎转载【点赞】,转载请保留此段声明,且在文章页面明显位置给出原文连接,谢谢。
    <br>关注个人公众号,定时同步更新技术及职场文章
<br><br>
   </p>
</div><br><br>
来源:https://www.cnblogs.com/hsiang/p/19290918
頁: [1]
查看完整版本: 推荐一种手动设置异步线程等待机制的解决方案