C# 从非异步代码调用异步方法
我正在更新一个库,它有一个内置于.NET3.5中的API界面。因此,所有方法都是同步的。我无法更改API(即,将返回值转换为任务),因为这将要求所有调用者都更改。所以我只剩下如何以同步方式最好地调用异步方法。这在ASP.NET 4、ASP.NET Core和.NET/.NET Core控制台应用程序的上下文中 我可能还不够清楚-情况是我现有的代码不支持异步,我想使用新的库,如System.Net.Http和AWS SDK,它们只支持异步方法。因此,我需要弥合这一差距,并且能够拥有可以同步调用但可以在其他地方调用异步方法的代码 我已经读了很多书,并且有很多次这被问到和回答 问题是大多数答案都不一样!我见过的最常用的方法是使用.Result,但这可能会导致死锁。我已经尝试了下面的所有方法,它们都很有效,但我不确定哪种方法是避免死锁、具有良好性能和良好运行时性能的最佳方法(在尊重任务调度器、任务创建选项等方面)。有明确的答案吗?最好的方法是什么C# 从非异步代码调用异步方法,c#,asp.net,multithreading,asynchronous,task-parallel-library,C#,Asp.net,Multithreading,Asynchronous,Task Parallel Library,我正在更新一个库,它有一个内置于.NET3.5中的API界面。因此,所有方法都是同步的。我无法更改API(即,将返回值转换为任务),因为这将要求所有调用者都更改。所以我只剩下如何以同步方式最好地调用异步方法。这在ASP.NET 4、ASP.NET Core和.NET/.NET Core控制台应用程序的上下文中 我可能还不够清楚-情况是我现有的代码不支持异步,我想使用新的库,如System.Net.Http和AWS SDK,它们只支持异步方法。因此,我需要弥合这一差距,并且能够拥有可以同步调用但可
private static T taskSyncRunner<T>(Func<Task<T>> task)
{
T result;
// approach 1
result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult();
// approach 2
result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult();
// approach 3
result = task().ConfigureAwait(false).GetAwaiter().GetResult();
// approach 4
result = Task.Run(task).Result;
// approach 5
result = Task.Run(task).GetAwaiter().GetResult();
// approach 6
var t = task();
t.RunSynchronously();
result = t.Result;
// approach 7
var t1 = task();
Task.WaitAll(t1);
result = t1.Result;
// approach 8?
return result;
}
private static T taskSyncRunner(Func任务)
{
T结果;
//方法1
结果=Task.Run(异步()=>await Task()).ConfigureAwait(错误).GetAwaiter().GetResult();
//方法2
结果=Task.Run(Task).ConfigureAwait(false).GetAwaiter().GetResult();
//方法3
结果=任务().ConfigureAwait(false).GetAwaiter().GetResult();
//方法4
结果=任务。运行(任务)。结果;
//方法5
结果=Task.Run(Task.GetAwaiter().GetResult();
//方法6
var t=task();
t、 同步运行();
结果=t.结果;
//方法7
var t1=任务();
Task.WaitAll(t1);
结果=t1。结果;
//方法8?
返回结果;
}
所以我只剩下如何以同步方式最好地调用异步方法
首先,这是一件可以做的事情。我之所以这样说,是因为在堆栈溢出中,指出这是魔鬼的行为是一种笼统的陈述,而不考虑具体情况是很常见的
它不需要一直异步才能正确。阻止某个异步对象使其同步可能会带来性能成本,这可能很重要,也可能完全不相关。这取决于具体情况
死锁来自两个试图同时进入同一单线程同步上下文的线程。任何能够可靠地避免这种情况的技术都可以避免阻塞造成的死锁
在这里,您对.ConfigureAwait(false)
的所有调用都是无意义的,因为您没有等待
RunSynchronously
无法使用,因为并非所有任务都可以通过这种方式处理
.GetAwaiter().GetResult()
不同于Result/Wait()
,因为它模拟了Wait
异常传播行为。你需要决定你是否想要。(因此,请研究这种行为是什么;无需在此重复。)
除此之外,所有这些方法都具有相似的性能。他们会以这样或那样的方式分配操作系统事件并阻止它。这是最昂贵的部分。我不知道哪种方法绝对是最便宜的
我个人喜欢Task.Run(()=>DoSomethingAsync()).Wait()代码>模式非常简单,因为它从分类上避免了死锁,并且不会隐藏一些GetResult()
可能隐藏的异常。但是您也可以将GetResult()
与此一起使用
我正在更新一个库,它有一个内置于.NET3.5中的API界面。因此,所有方法都是同步的。我无法更改API(即,将返回值转换为任务),因为这将要求所有调用者都更改。所以我只剩下如何以同步方式最好地调用异步方法
没有通用的“最佳”方式来执行异步反模式上的同步。只有各种各样的黑客都有自己的缺点
我建议您保留旧的同步API,然后在它们旁边引入异步API。您可以使用
首先,简要说明示例中每种方法的问题:
ConfigureAwait
只有在存在await
时才有意义;否则,它将一事无成
结果
将异常包装在一个聚合异常中
;如果必须阻止,请改用GetAwaiter().GetResult()
Task.Run
将在线程池线程上执行其代码(显然)。只有当代码可以在线程池线程上运行时,这才是好的
RunSynchronously
是一种高级API,在执行基于任务的动态并行时,在极少数情况下使用。你根本不在那种情况下
带有单个任务的Task.WaitAll
与仅Wait()
相同
async()=>await x
只是说()=>x
的一种效率较低的方式
阻止从当前线程启动的任务
以下是分类:
// Problems (1), (3), (6)
result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (1), (3)
result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (1), (7)
result = task().ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (2), (3)
result = Task.Run(task).Result;
// Problems (3)
result = Task.Run(task).GetAwaiter().GetResult();
// Problems (2), (4)
var t = task();
t.RunSynchronously();
result = t.Result;
// Problems (2), (5)
var t1 = task();
Task.WaitAll(t1);
result = t1.Result;
与这些方法中的任何一种不同的是,既然您已经有了可用的同步代码,那么您应该将其与较新的自然异步代码一起使用。例如,如果您的现有代码使用了WebClient
:
public string Get()
{
using (var client = new WebClient())
return client.DownloadString(...);
}
如果您想添加一个异步API,那么我会这样做:
private async Task<string> GetCoreAsync(bool sync)
{
using (var client = new WebClient())
{
return sync ?
client.DownloadString(...) :
await client.DownloadStringTaskAsync(...);
}
}
public string Get() => GetCoreAsync(sync: true).GetAwaiter().GetResult();
public Task<string> GetAsync() => GetCoreAsync(sync: false);
使用这种方法,您的逻辑将进入Core
方法,这些方法可以同步或异步运行(由sync
参数确定)。如果sync
为true
,则核心
private string GetCoreSync()
{
using (var client = new WebClient())
return client.DownloadString(...);
}
private static HttpClient HttpClient { get; } = ...;
private async Task<string> GetCoreAsync(bool sync)
{
return sync ?
GetCoreSync() :
await HttpClient.GetString(...);
}
public string Get() => GetCoreAsync(sync: true).GetAwaiter().GetResult();
public Task<string> GetAsync() => GetCoreAsync(sync: false);
public ActionResult Test()
{
TestClass result = Task.Run(async () => await GetNumbers()).GetAwaiter().GetResult();
return PartialView(result);
}
public async Task<TestClass> GetNumbers()
{
TestClass obj = new TestClass();
HttpResponseMessage response = await APICallHelper.GetData(Functions.API_Call_Url.GetCommonNumbers);
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
obj = JsonConvert.DeserializeObject<TestClass>(result);
}
return obj;
}