Вызов синхронного асинхронного метода

У меня есть метод 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;
}

Ответ 1

Вы можете получить доступ к свойству Result задачи, что приведет к блокировке потока до получения результата:

string code = GenerateCodeAsync().Result;

Примечание. В некоторых случаях это может привести к тупику: ваш вызов Result блокирует основной поток, тем самым предотвращая выполнение оставшейся части асинхронного кода. У вас есть следующие опции, чтобы убедиться, что этого не происходит:

Ответ 2

Вы должны получить awaiter (GetAwaiter()) и закончить ожидание завершения асинхронной задачи (GetResult()).

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

Ответ 3

Вы должны сделать это, используя делегаты, lambda expression

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" ;
    }

Ответ 4

Мне нужно вызвать этот метод синхронно.

Это возможно с помощью GenerateCodeAsync().Result или GenerateCodeAsync().Wait(), как показывает другой ответ. Это блокирует текущий поток до тех пор, пока GenerateCodeAsync не завершится.

Однако ваш вопрос помечен , и вы также оставили комментарий:

Я надеялся на более простое решение, думая, что обработано asp.net это намного проще, чем писать так много строк кода

Моя точка зрения: вы не должны блокировать асинхронный метод в ASP.NET. Это уменьшит масштабируемость вашего веб-приложения и может создать тупик (когда продолжение await внутри GenerateCodeAsync отправлено на AspNetSynchronizationContext). Используя Task.Run(...).Result, чтобы выгрузить что-то в поток пула, а затем блок еще больше повредит масштабируемость, поскольку он обрабатывает +1 больше потока для обработки заданного HTTP-запроса.

ASP.NET имеет встроенную поддержку асинхронных методов либо через асинхронные контроллеры (в ASP.NET MVC и Web API), либо напрямую через AsyncManager и PageAsyncTask в классическом ASP.NET. Вы должны использовать его. Подробнее см. этот ответ.

Ответ 5

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

Ответ 6

Чтобы предотвратить Task.Run() блокировки, я всегда стараюсь использовать Task.Run() когда мне нужно синхронно вызывать асинхронный метод, о котором упоминает @Heinzi.

Однако метод должен быть модифицирован, если асинхронный метод использует параметры. Например Task.Run(GenerateCodeAsync("test")).Result выдает ошибку:

Аргумент 1: невозможно преобразовать из " System.Threading.Tasks.Task<string> " в "System.Action"

Это можно назвать так:

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

Ответ 7

Другой способ может быть, если вы хотите дождаться завершения задачи:

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

Ответ 8

Если у вас есть асинхронный метод RefreshList, вы можете вызвать этот асинхронный метод из неасинхронного метода, как показано ниже.

Task.Run(async () => { await RefreshList(); });