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

当前回答

编辑:

Task有一个Wait方法Task.Wait(),它等待“promise”解析,然后继续执行,从而将其呈现为同步的。 例子:


async Task<String> MyAsyncMethod() { ... }

String mySyncMethod() {

    return MyAsyncMethod().Wait();
}

其他回答

这个线程上的大多数答案要么很复杂,要么会导致死锁。

下面的方法很简单,它将避免死锁,因为我们正在等待任务完成,然后才得到它的结果-

var task = Task.Run(() => GenerateCodeAsync()); 
task.Wait();
string code = task.Result;

此外,这里有一篇引用MSDN的文章,讨论了完全相同的事情- https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result-in-main-context/

您应该获取一个等待器(GetAwaiter()),并结束异步任务完成的等待(GetResult())。

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

我正在使用这种方法,它也将处理和传播来自底层异步任务的异常。

    private string RunSync()
    {
        var task = Task.Run(async () => await GenerateCodeService.GenerateCodeAsync());
        if (task.IsFaulted && task.Exception != null)
        {
            throw task.Exception;
        }

        return task.Result;
    }

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

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

参数1:不能从'System.Threading.Tasks.Task<string>'转换 的系统。行动”

它可以被这样调用:

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

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