我应该将任务包装在另一个任务中,还是应该只返回创建的任务?

时间:2014-02-27 09:05:41

标签: c# .net .net-4.0 ado.net task-parallel-library

我正在构建一个使用ADO.NET的.NET 4.0应用程序,所以我不能使用async / await。 我不想要一个解决方案,但我想知道以下哪些实现最好,为什么。我的单元测试通过了所有三个实现,但我想知道这三个实现之间的区别。

#1嵌套任务

在我的第一个实现中,我将任务包装在另一个任务中。我认为搞两个任务对性能不利,但我不确定。

public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
    return Task.Factory.StartNew(() =>
    {
        var sqlCommand = CheckIfSqlCommand(dbCommand);
        PrepareExecuteReader(dbCommand);

        return Task<IDataReader>
            .Factory
            .FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null)
            .Result;
    }, cancellationToken);
}

#2使用TaskCompletionSource

然后我尝试将结果包装在TaskCompletionSource中,所以我只有一个任务。

public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
    var taskCompletionSource = new TaskCompletionSource<IDataReader>();
    var sqlCommand = CheckIfSqlCommand(dbCommand);
    PrepareExecuteReader(dbCommand);

    var reader = Task<IDataReader>
        .Factory
        .FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null)
        .Result;

    taskCompletionSource.SetResult(reader);

    return taskCompletionSource.Task;
}

#3直接返回任务

我的最终解决方案是直接返回我创建的任务而不是包装它。

public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
    var sqlCommand = CheckIfSqlCommand(dbCommand);
    PrepareExecuteReader(dbCommand);

    return Task<IDataReader>
        .Factory
        .FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null);
}

基本上我的问题是:

我应该使用什么选项,或者有更好的方法吗?

2 个答案:

答案 0 :(得分:3)

你的#3是最好的。前两个没有理由引入并发症。

1可能会添加另一个线程,纯粹是为了异步运行CheckIfSqlCommand()PrepareExecuteReader()。这可能是你想要的,但它们听起来不像是需要很长时间的命令。

任务的2个引用.Result,它将在任务完成之前阻塞,因此无法完成使用任务的全部目的。

答案 1 :(得分:0)

我们使用Tasks异步编程有两个场景,一个是海量计算,另一个是I/O
在大规模计算的情况下,我们总是使用Task.Run从线程池中请求一个线程以避免阻塞线程。
I/O 情况下,如果没有提供 async api,我们总是使用 TaskCompletionSourceTask.Factory.FromAsync 来构建 async 方法。我认为混合这两个不是一个好的解决方案。
顺便说一句,Task.Run 一直用于客户端应用程序中,服务器端由于并发请求一般不使用Task.Run
这里有一篇很好的文章,你可以参考:
https://docs.microsoft.com/en-us/archive/msdn-magazine/2010/september/async-tasks-simplify-asynchronous-programming-with-tasks