首页 > C# > 如何在C#中从同步方法调用异步方法?

如何在C#中从同步方法调用异步方法?

上一篇 下一篇

我有一个想从同步方法调用的方法。到目前为止,我从 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

  1. 同步方法调用异步方法,获取 .Task
  2. 同步方法对 执行阻塞等待。Task
  3. 该方法使用不带 .asyncawaitConfigureAwait
  4. 在这种情况下无法完成,因为它仅在方法完成时完成;该方法无法完成,因为它正在尝试将其延续计划到 ,并且 WinForms/WPF/SL/ASP.NET 将不允许继续运行,因为同步方法已在该上下文中运行。TaskasyncasyncSynchronizationContext

这就是为什么尽可能在每种方法中使用是一个好主意的原因之一。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#】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 511次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 02-17
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
单页式简历模板 微信文章 自适应 响应式 微信公众平台 html5 微信图片 企业网站 微信模板 微信素材
您可能会喜欢的其他模板