.jpg)
我有一个想从同步方法调用的方法。到目前为止,我从 MSDN 文档中看到的只是通过异步方法调用异步方法,但我的整个程序不是使用异步方法构建的。public async void Foo()
这可能吗?
下面是从异步方法调用这些方法的一个示例:演练:
使用 Async 和 Await 访问 Web(C# 和 Visual Basic)
现在,我正在研究从同步方法调用这些异步方法。
网友回答:
添加一个最终解决我问题的解决方案,希望可以节省某人的时间。
首先阅读斯蒂芬·克利里的几篇文章:
从“不要阻止异步代码”中的“两个最佳实践”来看,第一个对我不起作用,第二个不适用(基本上如果我可以使用,我会!await
所以这是我的解决方法:将调用包装在一个里面,希望不再死锁。Task.Run<>(async () => await FunctionAsync());
这是我的代码:
public class LogReader
{
ILogger _logger;
public LogReader(ILogger logger)
{
_logger = logger;
}
public LogEntity GetLog()
{
Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
return task.Result;
}
public async Task<LogEntity> GetLogAsync()
{
var result = await _logger.GetAsync();
// more code here...
return result as LogEntity;
}
}
网友回答:
异步编程确实通过代码库“增长”。它被比作僵尸病毒。最好的解决方案是让它增长,但有时这是不可能的。
我在我的Nito.AsyncEx库中编写了一些类型来处理部分异步的代码库。但是,没有适用于所有情况的解决方案。
解决方案 A
如果你有一个简单的异步方法,不需要同步回其上下文,那么你可以使用:Task.WaitAndUnwrapException
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
您不想使用 或者因为它们将异常包装在 中。Task.WaitTask.ResultAggregateException
仅当不同步回其上下文时,此解决方案才适用。换句话说,每个 in 都应以 结尾。这意味着它无法更新任何 UI 元素或访问 ASP.NET 请求上下文。MyAsyncMethodawaitMyAsyncMethodConfigureAwait(false)
解决方案 B
如果确实需要同步回其上下文,则可以使用 提供嵌套上下文:MyAsyncMethodAsyncContext.RunTask
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
*更新 4/14/2014:在最新版本的库中,API 如下所示:
var result = AsyncContext.Run(MyAsyncMethod);
(可以在此示例中使用,因为会传播异常)。Task.ResultRunTaskTask
您可能需要而不是的原因是因为WinForms/WPF/SL/ASP.NET上发生了相当微妙的死锁可能性:AsyncContext.RunTaskTask.WaitAndUnwrapException
TaskTaskasyncawaitConfigureAwaitTaskasyncasyncSynchronizationContext这就是为什么尽可能在每种方法中使用是一个好主意的原因之一。ConfigureAwait(false)async
解决方案 C
AsyncContext.RunTask并非在所有情况下都有效。例如,如果该方法等待需要 UI 事件才能完成的内容,则即使使用嵌套上下文也会死锁。在这种情况下,您可以在线程池上启动该方法:asyncasync
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
但是,此解决方案需要 将在线程池上下文中工作。因此,它无法更新 UI 元素或访问 ASP.NET 请求上下文。在这种情况下,您也可以添加到其语句中,并使用解决方案 A。MyAsyncMethodConfigureAwait(false)await
更新,2019-05-01:当前的“最不差做法”在此处的 MSDN 文章中。
网友回答:
Microsoft 构建了一个 AsyncHelper(内部)类来将 Async 作为 Sync 运行。源如下所示:
internal static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return AsyncHelper._myTaskFactory
.StartNew<Task<TResult>>(func)
.Unwrap<TResult>()
.GetAwaiter()
.GetResult();
}
public static void RunSync(Func<Task> func)
{
AsyncHelper._myTaskFactory
.StartNew<Task>(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
Microsoft.AspNet.Identity 基类只有异步方法,为了将它们调用为 Sync,有一些类的扩展方法看起来像(示例用法):
public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}
public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}
对于那些关心代码许可条款的人,这里有一个非常相似的代码的链接(只是在线程上添加了对文化的支持),其中包含注释以指示它是 Microsoft 许可的 MIT。https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs
这不和只调用 Task.Run(async ()=> await AsyncFunc()) 一样吗?结果?AFAIK,微软现在不鼓励调用TaskFactory.StartNew,因为它们都是等效的,并且一个比另一个更具可读性。
绝对不行。
简单的答案是
.Unwrap().GetAwaiter().GetResult() != .Result
首先关闭
任务结果是否与 相同。GetAwaiter.GetResult()?
其次。unwrap() 会导致任务的设置不阻止包装的任务。
这应该导致任何人问
这不和只调用 Task.Run(async ()=> await AsyncFunc()) 一样吗?GetAwaiter()。GetResult()
然后这将是一个 视情况而定.
关于 Task.Start() 、Task.Run() 和 Task.Factory.StartNew() 的使用
摘录:
Task.Run 使用 TaskCreationOptions.DenyChildAttach 这意味着子任务不能附加到父任务,它使用 TaskScheduler.Default,这意味着在线程池上运行任务的任务将始终用于运行任务。
Task.Factory.StartNew 使用 TaskScheduler.Current 表示当前线程的调度程序,它可能是 TaskScheduler.Default,但并非总是如此。
延伸阅读:
指定同步上下文
ASP.NET 核心同步上下文
为了额外的安全性,这样称呼它不是更好吗 这样我们告诉“内部”方法“请不要尝试同步到上层上下文并取消锁定”
AsyncHelper.RunSync(async () => await AsyncMethod().ConfigureAwait(false));
真的很棒,正如大多数对象架构问题一样,这取决于。
作为一种扩展方法,你是想绝对每次调用都强制这样做,还是让使用该函数的程序员在他们自己的异步调用上配置它?我可以看到调用三个场景的用例;它很可能不是您在 WPF 中想要的东西,在大多数情况下当然是有意义的,但考虑到 ASP.Net Core 中没有上下文,如果您可以保证它是 ASP.Net Core 的内部上下文,那么这并不重要。
模板简介:该模板名称为【如何在C#中从同步方法调用异步方法?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。