王小五 發表於 2025-12-28 10:17:39

.NET中TaskCompletionSource的用法小结

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">TaskCompletionSource&lt;T&gt;的基本概念</a></li><li><a href="#_label1">TaskCompletionSource&lt;T&gt;类的构造与使用</a></li><li><a href="#_label2">例子:使用TaskCompletionSource完成一个异步任务</a></li><li><a href="#_label3">线程安全</a></li><li><a href="#_label4">总结</a></li></ul></div><p><code>TaskCompletionSource&lt;T&gt;</code> 是 .NET 中用于控制和管理异步任务 (<code>Task</code>) 的类,广泛用于在异步编程中手动控制任务的完成。它通常与 <code>async</code> 和 <code>await</code> 配合使用,但它本身是用来创建、设置或操作任务的结果的。</p>
<p class="maodian"><a name="_label0"></a></p><h2>TaskCompletionSource&lt;T&gt;的基本概念</h2>
<p>在标准的异步编程中,你会看到异步方法返回一个 <code>Task</code> 或 <code>Task&lt;T&gt;</code> 对象,它代表着一个尚未完成的操作。而 <code>TaskCompletionSource&lt;T&gt;</code> 类允许你手动设置这些任务的状态,例如让任务完成、失败或取消。</p>
<p class="maodian"><a name="_label1"></a></p><h2>TaskCompletionSource&lt;T&gt;类的构造与使用</h2>
<ol><li><p>构造函数</p>
<ul><li>TaskCompletionSource():创建一个新的 TaskCompletionSource&lt;T&gt; 实例。</li><li>TaskCompletionSource(CancellationToken cancellationToken):带有取消标记的构造函数。</li></ul>
<p>TaskCompletionSource&lt;T&gt; 本身并不直接执行任务,它仅提供了一些方法来控制任务的状态。</p></li><li><p>关键属性</p>
<ul><li>Task:TaskCompletionSource&lt;T&gt; 类有一个公开的属性 Task,它返回一个 Task&lt;T&gt; 实例,表示这个任务的状态。你可以通过这个属性来跟踪任务的状态,并通过 await 来等待其完成。</li><li>Task.IsCompleted:判断任务是否完成。</li><li>Task.IsFaulted:判断任务是否因错误而失败。</li><li>Task.IsCanceled:判断任务是否被取消。</li></ul></li><li><p>关键方法</p>
<ul><li>SetResult(T result):完成任务并提供结果。如果任务已完成(无论是成功、失败还是取消),这会引发异常。</li><li>SetException(IEnumerable&lt;Exception&gt; exceptions):让任务以失败的状态完成,并报告异常。</li><li>SetException(Exception exception):让任务以失败的状态完成,并报告单个异常。</li><li>SetCanceled():将任务标记为已取消状态。</li><li>TrySetResult(T result):类似于 SetResult,但它不会抛出异常,如果任务已经完成(无论是成功、失败还是取消),会返回 false。</li><li>TrySetException(IEnumerable&lt;Exception&gt; exceptions):与 SetException 类似,但如果任务已经完成,则返回 false。</li><li>TrySetException(Exception exception):类似于 SetException,但如果任务已完成,则返回 false。</li><li>TrySetCanceled():与 SetCanceled 类似,但如果任务已经完成,则返回 false。</li></ul></li><li><p>常用场景<br />TaskCompletionSource&lt;T&gt; 主要用于那些你无法直接控制任务完成的场景,或者在一些复杂的异步控制流中。以下是一些常见的使用场景:</p>
<ul><li><p>异步回调转换:如果你有一个基于回调的异步API,但你想使用 Task 来控制它的执行,可以使用 TaskCompletionSource&lt;T&gt;。例如,假设你在处理一个异步的文件读写操作,并且你想将其转换为 Task:</p>
<div class="jb51code"><pre class="brush:csharp;">public Task&lt;string&gt; ReadFileAsync(string path)
{
    var tcs = new TaskCompletionSource&lt;string&gt;();

    // 模拟异步操作的回调
    File.ReadAllTextAsync(path).ContinueWith(task =&gt;
    {
      if (task.IsFaulted)
      {
            tcs.SetException(task.Exception);// 设置异常
      }
      else if (task.IsCanceled)
      {
            tcs.SetCanceled();// 设置取消
      }
      else
      {
            tcs.SetResult(task.Result);// 设置结果
      }
    });

    return tcs.Task;
}
</pre></div></li><li><p><strong>手动控制异步任务的完成</strong>:有时在异步操作中,任务的完成是由某个外部事件触发的。你可以使用 <code>TaskCompletionSource&lt;T&gt;</code> 来手动完成任务。例如,基于某个标志位或者外部事件来触发任务的完成:</p>
<div class="jb51code"><pre class="brush:csharp;">public Task WaitForExternalEventAsync()
{
    var tcs = new TaskCompletionSource&lt;object&gt;();

    // 假设有一个外部事件
    ExternalEventTriggered += (sender, e) =&gt;
    {
      tcs.SetResult(null);// 当事件触发时,设置任务完成
    };

    return tcs.Task;
}
</pre></div></li><li><p><strong>取消支持的异步操作</strong>:你可以将 <code>TaskCompletionSource&lt;T&gt;</code> 与 <code>CancellationToken</code> 配合使用,在任务中支持取消。例如:</p>
<div class="jb51code"><pre class="brush:csharp;">public Task&lt;string&gt; LongRunningTask(CancellationToken cancellationToken)
{
    var tcs = new TaskCompletionSource&lt;string&gt;();

    cancellationToken.Register(() =&gt; tcs.SetCanceled());// 任务取消时标记为已取消

    Task.Run(() =&gt;
    {
      try
      {
            // 模拟长时间运行的操作
            Thread.Sleep(5000);

            if (!cancellationToken.IsCancellationRequested)
            {
                tcs.SetResult("Task Completed");
            }
      }
      catch (Exception ex)
      {
            tcs.SetException(ex);
      }
    });

    return tcs.Task;
}
</pre></div></li></ul></li></ol>
<p class="maodian"><a name="_label2"></a></p><h2>例子:使用TaskCompletionSource完成一个异步任务</h2>
<div class="jb51code"><pre class="brush:csharp;">public Task&lt;string&gt; GetDataAsync()
{
    var tcs = new TaskCompletionSource&lt;string&gt;();

    // 模拟异步操作
    Task.Run(() =&gt;
    {
      try
      {
            // 假设在这里获取数据
            string data = "Hello, world!";
            tcs.SetResult(data);// 操作成功,设置结果
      }
      catch (Exception ex)
      {
            tcs.SetException(ex);// 操作失败,设置异常
      }
    });

    return tcs.Task;
}
</pre></div>
<p class="maodian"><a name="_label3"></a></p><h2>线程安全</h2>
<p>TaskCompletionSource&lt;T&gt; 是线程安全的,可以在多个线程中安全地调用其方法,例如 SetResult、SetException 等。不过,你只应该在任务完成后调用一次这些方法,否则会引发 InvalidOperationException。</p>
<p class="maodian"><a name="_label4"></a></p><h2>总结</h2>
<p>TaskCompletionSource&lt;T&gt; 是一个非常强大的工具,允许开发者以更加灵活的方式控制和管理异步任务。它让你能够将基于回调的异步编程模型转化为更加直观和易于管理的 Task 模型,同时还提供了手动控制任务状态(完成、失败或取消)的能力。在处理复杂异步控制流时,TaskCompletionSource&lt;T&gt; 是非常有用的。</p>
頁: [1]
查看完整版本: .NET中TaskCompletionSource的用法小结