【问题标题】:TaskCompletionSource usage in IO Async methodsIO Async 方法中的 TaskCompletionSource 使用
【发布时间】:2016-01-22 10:04:40
【问题描述】:

System.Data.SqlClient.SqlCommandExecuteNonQueryAsync()方法的实现如下:

    public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken) {

        Bid.CorrelationTrace("<sc.SqlCommand.ExecuteNonQueryAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        SqlConnection.ExecutePermission.Demand();   

        TaskCompletionSource<int> source = new TaskCompletionSource<int>();

        CancellationTokenRegistration registration = new CancellationTokenRegistration();
        if (cancellationToken.CanBeCanceled) {
            if (cancellationToken.IsCancellationRequested) {
                source.SetCanceled();
                return source.Task;
            }
            registration = cancellationToken.Register(CancelIgnoreFailure);
        }

        Task<int> returnedTask = source.Task;
        try {
            RegisterForConnectionCloseNotification(ref returnedTask);

            Task<int>.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) => {
                registration.Dispose();
                if (t.IsFaulted) {
                    Exception e = t.Exception.InnerException;
                    source.SetException(e);
                }
                else {
                    if (t.IsCanceled) {
                        source.SetCanceled();
                    }
                    else {
                        source.SetResult(t.Result);
                    }
                }
            }, TaskScheduler.Default);
        } 
        catch (Exception e) {
            source.SetException(e);
        }

        return returnedTask;
    }

我总结为:

  1. 创建TaskCompletionSource&lt;int&gt; source = new TaskCompletionSource&lt;int&gt;();
  2. 使用 Task&lt;int&gt;.Factory.FromAsync 创建一个新任务,使用 APM“Begin/End”API
  3. 任务完成时调用source.SetResult()
  4. 返回source.Task

这里使用TaskCompletionSource有什么意义,为什么不直接返回Task&lt;int&gt;.Factory.FromAsync()创建的任务呢?此任务还包含结果和异常(如果有)。

在 C# in a Nutshell book 的 Asynchronous Programming and Continuations 部分中,它指出:

在编写延迟时,我们 使用了TaskCompletionSource,这是实现“底层”的标准方式 I/O 绑定的异步方法。

对于计算绑定方法,我们使用 Task.Run 来启动线程绑定并发。 只需将任务返回给调用者,我们就创建了一个异步方法。

为什么使用Task.Run() 可以实现计算绑定的方法,而不能使用I/O 绑定的方法?

【问题讨论】:

    标签: c# taskcompletionsource


    【解决方案1】:

    请注意,要获得明确的答案,您必须询问代码的作者。除此之外,我们只能推测。但是,我认为以合理的准确性做出一些推论是合理的……

    这里使用TaskCompletionSource有什么意义,为什么不直接返回Task.Factory.FromAsync()创建的任务呢?

    在这种情况下,在我看来,主要原因是允许实现在任务实际完成之前取消注册注册的回调CancelIgnoreFailure()。这可确保在客户端代码收到完成通知时,API 本身已完全从操作中清除。

    次要原因可能只是为了提供完整的抽象。 IE。不允许任何底层实现从方法中“泄漏”,以Task 对象的形式,调用者可能会检查或(更糟)以干扰正确的方式进行操作任务的可靠运行。

    为什么使用Task.Run()可以实现计算绑定的方法,而不能使用I/O绑定的方法?

    可以使用Task.Run() 实现I/O 绑定操作,但您为什么要这样做?这样做会向操作提交一个线程,这对于原本不需要线程的操作来说是浪费的。

    I/O 绑定操作通常得到 I/O 完成端口和 IOCP 线程池(其中的线程处理任意数量的 IOCP 的完成)的支持,因此简单地使用现有的异步会更有效I/O API,而不是使用Task.Run() 来调用同步I/O 方法。

    【讨论】:

    • 我能不能说,使用TaskCompletionSource其实是为了统一方法的输出,所以把异常展平,结果设置等等。这种情况下,方法内部的线程机制可以是任何东西.
    • “方法内部的线程机制可以是任何东西”——只要方法异步运行,可以。在这里使用 TCS 允许公共方法成为其中实际发生的任何内容的抽象,确保调用者不依赖,甚至不知道具体的实现细节(即良好的实现隐藏实践)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多