使用带有Result的异步版本实现功能

时间:2016-07-26 10:50:35

标签: c# async-await

我正在实现函数的异步和普通版本。我已经实现了异步版本。对于普通版本,我可以使用正常的对应部分复制粘贴和替换异步函数调用的使用。或者我可以使用Result调用异步版本,如下所示。

第一种方法:

public int SomeTask(int param) 
{
   //Something going on
   return SomeOtherTask();
} 

public async Task<int> SomeTaskAsync(int param) 
{
   //Something going on (copy pasted)
   return await SomeOtherTaskAsync();    
} 

第二种方法:

public int SomeTask(int param) 
{
   return SomeTaskAsync(param).Result;
} 

public async Task<int> SomeTaskAsync(int param) 
{
   //some function calls with await
} 

第二种方法可能存在问题吗?

2 个答案:

答案 0 :(得分:2)

性能将受到影响。多少是不可能的 - 你是代码中的那个,你可以为你的确切用例进行分析。

但是,制作仅调用Result的方法的同步版本没有意义 - 您的方法的用户可以做同样的事情。问题是,无论如何都要做到这一点非常危险,尤其是在涉及同步上下文时。考虑示例代码:

async void btnTest_Click(object sender, EventArgs e)
{
  await DoSomethingAsync();
}

async Task DoSomethingAsync()
{
  await Task.Delay(1000);
}

这很好用。现在,让我们试试你的“同步”版本:

async void btnTest_Click(object sender, EventArgs e)
{
  DoSomethingAsync().Result;
}

async Task DoSomethingAsync()
{
  await Task.Delay(1000);
}
哎呀,你有一个僵局。 UI线程正在等待DoSomethingAsync完成,但DoSomethingAsync需要在UI线程上完成执行。如果你同步等待它,你永远不能假设async方法会运行。

此外,通过使用Result而不是await,您将失去许多异常处理功能。例如,异常堆栈跟踪将全部搞砸,您将需要处理由创建任务的方法和Result调用本身抛出的异常 - 第一个将异常抛出到实际上必须等待的第一个await的点,以及所有延续的第二个点。你永远不知道哪个是哪个。

答案 1 :(得分:0)

首先,如果您有自然异步操作,则应该公开异步API。正如Damien指出的那样,"Should I expose synchronous wrappers for my asynchronous methods?"的答案是&#34; No&#34;。

这些包装器的一个问题是 没有模式适用于所有场景!我在文章中描述了各种同步异步黑客攻击在Brownfield Async。他们每个人都有缺点;正如Luaan指出的那样,阻塞黑客有可能陷入僵局。

但是,如果您有充分的理由这样做(即,您的库历史上有同步方法,并且您正在添加异步方法但希望保持同步方法以实现向后兼容性,至少对于版本或两个),然后你可以使用&#34;布尔标志hack&#34;正如我的文章所述。

斯蒂芬·图布不久前向我展示了这个伎俩。这个想法是(私有)实现函数采用bool sync参数,如果是true,那么它返回的任务保证已经完成。这可以避免定期阻塞的死锁问题:

private async Task<int> SomeTaskAsync(int param, bool sync)
{
  // Every `await` in your code needs to honor `sync`
  if (sync)
    return SomeOtherTask();
  else
    return await SomeOtherTaskAsync();
}

public int SomeTask(int param) 
{
  return SomeTaskAsync(param, sync: true).GetAwaiter().GetResult();
} 

public Task<int> SomeTaskAsync(int param) 
{
  return SomeTaskAsync(param, sync: false);
}