我正在构建一个使用ADO.NET的.NET 4.0应用程序,所以我不能使用async / await。 我不想要一个解决方案,但我想知道以下哪些实现最好,为什么。我的单元测试通过了所有三个实现,但我想知道这三个实现之间的区别。
在我的第一个实现中,我将任务包装在另一个任务中。我认为搞两个任务对性能不利,但我不确定。
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);
}
然后我尝试将结果包装在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;
}
我的最终解决方案是直接返回我创建的任务而不是包装它。
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);
}
我应该使用什么选项,或者有更好的方法吗?
答案 0 :(得分:3)
你的#3是最好的。前两个没有理由引入并发症。
1可能会添加另一个线程,纯粹是为了异步运行CheckIfSqlCommand()
和PrepareExecuteReader()
。这可能是你想要的,但它们听起来不像是需要很长时间的命令。
任务的2个引用.Result
,它将在任务完成之前阻塞,因此无法完成使用任务的全部目的。
答案 1 :(得分:0)
我们使用Tasks
异步编程有两个场景,一个是海量计算,另一个是I/O
。
在大规模计算的情况下,我们总是使用Task.Run
从线程池中请求一个线程以避免阻塞线程。
在 I/O
情况下,如果没有提供 async
api,我们总是使用 TaskCompletionSource
或 Task.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