1

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

    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<int> source = new TaskCompletionSource<int>();
  2. Task<int>.Factory.FromAsync使用 APM "Begin/End" API创建一个新任务
  3. source.SetResult()任务完成时调用。
  4. 返回source.Task

在这里使用有什么意义,TaskCompletionSource为什么不直接返回由创建的任务Task<int>.Factory.FromAsync()?此任务还包装了结果和异常(如果有)。

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

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

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

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

4

1 回答 1

3

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

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

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

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

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

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

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

于 2016-01-23T02:03:16.247 回答