Task.ContinueWith混乱

时间:2012-11-30 18:58:53

标签: asynchronous task .net-4.5 async-await

使用ASP.NET 4.5我正在尝试使用新的async / await玩具。我有一个IDataReader实现类,它包含特定于供应商的读者(如SqlDatareader)。我有一个简单的ExecuteSql()方法,它同步操作:

public IDataReader ReaderForSql(string sql)
{
    var cmd = NewCommand(sql, CommandType.Text);
    return DBReader.ReaderFactory(cmd.ExecuteReader());
}

我想要的是这个的异步版本。这是我的第一次尝试:

public Task<IDataReader> ReaderForSqlAsync(string sql, CancellationToken ct)
{
    var cmd = NewCommand(sql, CommandType.Text);
    return cmd.ExecuteReaderAsync(ct)
              .ContinueWith(t => DBReader.ReaderFactory(t.Result));
}

我用它:

using (var r = await connection.ReaderForSqlAsync("SELECT ...", cancellationToken))
{
    ...
}

到目前为止,这在我的有限测试中效果很好。但在观看了Cloud9视频几次之后:http://channel9.msdn.com/Events/aspConf/aspConf/Async-in-ASP-NET我对他们提出的警告感到不安:

  • 继续消耗额外的线程池资源 - Readerfactory很轻!
  • Task.Result blocking

因为我将ContinuationToken传递给ExecuteReaderAsync(),所以似乎取消只是ExecuteReaderAsync()失败的另一个原因(毕竟它是SQL!)

当我尝试继续使用它时,任务的状态是什么? t.Result会阻止吗?扔?做错了什么?

2 个答案:

答案 0 :(得分:4)

默认情况下,

ContinueWith将使用当前任务调度程序(线程池线程),但您可以通过传递TaskContinuationOptions.ExecuteSynchronously和显式TaskScheduler来更改它。

那就是说,我会把这作为第一次努力:

public async Task<IDataReader> ReaderForSqlAsync(string sql, CancellationToken ct)
{
  var cmd = NewCommand(sql, CommandType.Text);
  var readerResult = await cmd.ExecuteReaderAsync(ct).ConfigureAwait(false);
  return DBReader.ReaderFactory(readerResult);
}

asyncawait以一致的方式处理所有ContinueWith美食和边缘条件。如果性能测试表明这是一个严重的问题,可能会使此代码更加复杂

答案 1 :(得分:2)

如果任务尚未完成,

Result会阻止。但是在延续处理程序中它已经有了。所以它不会阻止。你正在做正确的事。

当您在出现故障的任务上调用Result时(并且您可能会发生这种情况),会重新抛出异常。这会导致您继续使用相同的异常进行故障,从而导致从ReaderForSqlAsync返回的最终任务也出现故障。这是一件好事:整个任务链出现故障并且已经观察到所有异常(与被吞噬相反)。所以这也是最好的做法。

使用线程进行计算绑定工作总是可以的。再说一遍,你使用ContinueWith做正确的事情。毕竟,您必须在某处计算IDataReader 。你无法计算它。