首页 文章

同步调用异步方法

提问于
浏览
186

我有一个 async 方法:

public async Task<string> GenerateCodeAsync()
{
    string code = await GenerateCodeService.GenerateCodeAsync();
    return code;
}

我需要从同步方法调用此方法 .

如何在不必复制 GenerateCodeAsync 方法的情况下执行此操作以使其同步工作?

Update

然而找不到合理的解决方案

但是,我看到 HttpClient 已经实现了这种模式

using (HttpClient client = new HttpClient())
{
    // async
    HttpResponseMessage responseAsync = await client.GetAsync(url);

    // sync
    HttpResponseMessage responseSync = client.GetAsync(url).Result;
}

6 回答

  • 15

    您可以访问任务的 Result 属性,这将导致您的线程阻塞,直到结果可用:

    string code = GenerateCodeAsync().Result;
    

    注意:在某些情况下,这可能会导致死锁:您对 Result 的调用会阻塞主线程,从而阻止执行其余的异步代码 . 您有以下选项可确保不会发生这种情况:

    string code = Task.Run(GenerateCodeAsync).Result;
    
  • 17

    你应该得到awaiter( GetAwaiter() )并结束等待完成异步任务( GetResult() ) .

    string code = GenerateCodeAsync().GetAwaiter().GetResult();
    
  • 27

    我需要从同步方法中调用此方法 .

    有可能是 GenerateCodeAsync().ResultGenerateCodeAsync().Wait() ,正如另一个答案所暗示的那样 . 这将阻止当前线程,直到 GenerateCodeAsync 完成 .

    但是,您的问题标有asp.net,您还留下了评论:

    我希望有一个更简单的解决方案,认为asp.net处理这个比编写这么多行代码要容易得多

    我的观点是,你_____1383_在ASP.NET中的异步方法 . 这将降低Web应用程序的可伸缩性,并可能造成死锁(当 GenerateCodeAsync 中的 await 延续发布到 AspNetSynchronizationContext 时) . 使用 Task.Run(...).Result 将某些内容卸载到池线程然后阻塞将进一步损害可伸缩性,因为它会产生一个以上的线程来处理给定的HTTP请求 .

    ASP.NET内置了对异步方法的支持,可以通过异步控制器(在ASP.NET MVC和Web API中),也可以直接通过经典ASP.NET中的 AsyncManagerPageAsyncTask . 你应该使用它 . 有关更多详细信息,请查看this answer .

  • 48

    为了防止死锁,我总是尝试使用 Task.Run() ,当我必须同步调用@Heinzi提到的异步方法时 .

    但是,如果异步方法使用参数,则必须修改该方法 . 例如 Task.Run(GenerateCodeAsync("test")).Result 给出错误:

    参数1:无法从'System.Threading.Tasks.Task <string>'转换为'System.Action'

    这可以像这样调用:

    string code = Task.Run(() => GenerateCodeAsync("test")).Result;
    
  • 3

    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();
        }
    

    所以,这是异步方法的包装器 . 请不要从结果中读取数据 - 它可能会阻止您在ASP中的代码 .

    还有另一种方式 - 这对我来说很可疑,但你也可以考虑它

    Result r = null;
    
                YourAsyncMethod()
                    .ContinueWith(t =>
                    {
                        r = t.Result;
                    })
                    .Wait();
    
  • 241

    您应该能够使用委托,lambda表达式完成此操作

    private void button2_Click(object sender, EventArgs e)
        {
    
            label1.Text = "waiting....";
    
            Task<string> sCode = Task.Run(async () =>
            {
                string msg =await GenerateCodeAsync();
                return msg;
            });
    
            label1.Text += sCode.Result;
    
        }
    
        private Task<string> GenerateCodeAsync()
        {
            return Task.Run<string>(() => GenerateCode());
        }
    
        private string GenerateCode()
        {
            Thread.Sleep(2000);
            return "I m back" ;
        }
    

相关问题