调用异步方法同步调用异步方法同步(Calling async method synchronousl

2019-05-09 02:29发布

我有一个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;
}

Answer 1:

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

string code = GenerateCodeAsync().Result;

注:在某些情况下,这可能会导致死锁:您的来电Result块为主线,从而防止异步代码来执行的剩余部分。 您有以下选项,以确保不会发生这种情况:

  • 加入.ConfigureAwait(false)到库法
  • 线程池中的线程执行明确的异步方法并等待它完成:

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


Answer 2:

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

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


Answer 3:

你应该能够得到这个工作使用委托,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" ;
    }


Answer 4:

我需要调用从同步方法,此方法。

这是可能的GenerateCodeAsync().ResultGenerateCodeAsync().Wait()因为对方的回答暗示。 这将阻止当前线程,直到GenerateCodeAsync已完成。

然而,你的问题被打上asp.net ,并且还留下了评论:

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

我的观点是,你不应该在ASP.NET异步方法来阻止 。 这将减少你的web应用程序的可扩展性,并可能产生死锁(当await中继续GenerateCodeAsync发布到AspNetSynchronizationContext )。 使用Task.Run(...).Result卸载东西池线程,然后块将更加伤害的可扩展性,因为它会带来更多的+1线程来处理一个给定的HTTP请求。

ASP.NET有内置的异步方法,既可以通过异步控制器(在ASP.NET MVC和Web API)或直接通过支持AsyncManagerPageAsyncTask经典ASP.NET。 你应该使用它。 欲了解更多详情,请查看这个答案 。



Answer 5:

微软身份具有扩展方法,其同步调用异步方法。 例如有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();


Answer 6:

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

然而,该方法具有,如果异步方法使用的参数进行修改。 例如Task.Run(GenerateCodeAsync("test")).Result给出了错误:

参数1:不能从转换“ System.Threading.Tasks.Task<string> ”到“System.Action”

这可以这样调用,而不是:

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


Answer 7:

另一种方法可能是,如果你想等到任务完成:

var t = GenerateCodeService.GenerateCodeAsync();
Task.WhenAll(t);
string code = t.Result;


文章来源: Calling async method synchronously