我有一个async方法:
public async Task<string> GenerateCodeAsync()
{
string code = await GenerateCodeService.GenerateCodeAsync();
return code;
}
我需要从一个同步方法调用这个方法。
我如何才能做到这一点,而不必复制GenerateCodeAsync方法,以使其同步工作?
更新
但没有找到合理的解决方案。
但是,我看到HttpClient已经实现了这个模式
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
你可以访问任务的Result属性,这将导致你的线程阻塞,直到结果可用:
string code = GenerateCodeAsync().Result;
注意:在某些情况下,这可能会导致死锁:对Result的调用阻塞主线程,从而阻止异步代码的其余部分执行。你有以下选项来确保这种情况不会发生:
添加.ConfigureAwait(false)到你的库方法或
显式地在线程池线程中执行async方法,并等待它完成:
string code = Task.Run(() => GenerateCodeAsync).Result;
这并不意味着你应该在所有异步调用之后盲目地添加.ConfigureAwait(false) !有关为什么以及何时应该使用.ConfigureAwait(false)的详细分析,请参阅以下博客文章:
.NET博客:ConfigureAwait FAQ
Microsoft Identity具有同步调用异步方法的扩展方法。
例如,有GenerateUserIdentityAsync()方法和CreateIdentity()方法
如果你查看UserManagerExtensions.CreateIdentity()
它是这样的:
public static ClaimsIdentity CreateIdentity<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user,
string authenticationType)
where TKey : IEquatable<TKey>
where TUser : class, IUser<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType));
}
现在让我们看看AsyncHelper。RunSync确实
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
var cultureUi = CultureInfo.CurrentUICulture;
var culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew(() =>
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap().GetAwaiter().GetResult();
}
这是async方法的包装器。
请不要从Result中读取数据——它可能会在ASP中阻塞你的代码。
还有另一种方法——对我来说有点可疑,但你也可以考虑一下
Result r = null;
YourAsyncMethod()
.ContinueWith(t =>
{
r = t.Result;
})
.Wait();