Использование функции async в функции синхронизации [duplicate]

Вы можете использовать словари для этого. Словари - это хранилища ключей и ценностей.

>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'y': 2, 'x': 1, 'z': 3}
>>> dct["y"]
2

Вы можете использовать имена переменных ключей для достижения эффекта переменных переменных без риска для безопасности.

>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'

В тех случаях, когда вы думаете сделать что-то вроде

var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...

список может быть более подходящим, чем dict. Список представляет упорядоченную последовательность объектов с целыми индексами:

l = ['foo', 'bar', 'baz']
print(l[1])           # prints bar, because indices start at 0
l.append('potatoes')  # l is now ['foo', 'bar', 'baz', 'potatoes']

Для упорядоченных последовательностей списки удобнее, чем dict с целыми ключами, потому что списки поддерживают итерацию в порядке индекса, slicing , append и другие операции, которые потребуют неудобного управления ключами с помощью dict.

527
задан dotctor 28 June 2015 в 22:40
поделиться

24 ответа

Небольшая заметка - этот подход:

Task<Customer> task = GetCustomers();
task.Wait()

работает для WinRT.

Позвольте мне объяснить:

private void TestMethod()
{
    Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
    task.Wait(); // wait executing the method
    var customer = task.Result; // get's result.
    Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
    public Customer()
    {
        new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
    }
    public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
    return Task.Run(() => new Customer
    {
        Name = "MyName"
    });
}

Кроме того, этот подход работает только для решений для Windows Store!

Примечание. Этот способ не является потокобезопасным, если вы вызываете свой метод внутри другого метода асинхронизации (согласно комментариям @Servy)

9
ответ дан animuson 17 August 2018 в 22:42
поделиться
  • 1
    Я объяснил это решение, проверьте раздел EDIT. – RredCat 16 December 2013 в 21:01
  • 2
    Это может очень легко привести к взаимоблокировкам при вызове в асинхронных ситуациях. – Servy 16 December 2013 в 21:07
  • 3
    @Servy имеет смысл. Так как я получаю правильное использование Wait (timeOut), может помочь, правильно? – RredCat 16 December 2013 в 22:49
  • 4
    Затем вам нужно беспокоиться о том, что тайм-аут будет достигнут, когда операция на самом деле не будет выполнена, что очень плохо, а также время, потраченное на ожидание до таймаута в случаях, когда он блокируется (и в этом случае вы еще продолжает, когда это не делается). Так что нет, это не устраняет проблему. – Servy 16 December 2013 в 22:50
  • 5
    @Servy Похоже, мне нужно реализовать CancellationToken для моего решения. – RredCat 16 December 2013 в 23:41

Это хорошо работает для меня

public static class TaskHelper
{
    public static void RunTaskSynchronously(this Task t)
    {
        var task = Task.Run(async () => await t);
        task.Wait();
    }

    public static T RunTaskSynchronously<T>(this Task<T> t)
    {
        T res = default(T);
        var task = Task.Run(async () => res = await t);
        task.Wait();
        return res;
    }
}
20
ответ дан Clement 17 August 2018 в 22:42
поделиться
  • 1
    Вам также нужно использовать метод Task.Unwrap , потому что ваш оператор Task.Wait вызывает ожидание внешней задачи (созданной Task.Run ), не для внутренней wait t Задача, переданная как параметр метода расширения. Ваш метод Task.Run возвращает не Task & lt; T & gt ;, но Task & lt; Task & lt; T & gt; gt ;. В некоторых простых сценариях ваше решение может работать из-за оптимизации TaskScheduler, например, используя метод TryExecuteTaskInline для выполнения задач в текущем потоке во время операции Wait . Пожалуйста, просмотрите мой комментарий к этот ответ. – sgnsajgon 15 September 2014 в 19:45
  • 2
    Это неверно. Task.Run вернет Task & lt; T & gt ;. См. Эту перегрузку msdn.microsoft.com/en-us/library/hh194918 (v = vs.110) .aspx – Clement 16 September 2014 в 00:02
  • 3
    Как это предполагается использовать? Это взаимоблокировки в WPF: MyAsyncMethod().RunTaskSynchronously(); – ygoe 26 September 2016 в 15:20
  • 4
    Огромное спасибо. – Prateek-Systematix 29 July 2017 в 09:00

Если я правильно читаю ваш вопрос - код, который хочет, чтобы синхронный вызов метода асинхронизации выполнялся в приостановленном потоке диспетчера. И вы хотите фактически синхронизировать этот поток до тех пор, пока не будет завершен метод async.

Асинхронные методы на C # 5 приводятся в действие путем эффективного измельчения метода на куски под капотом и возвращения Task, который может отслеживать общее завершение всего шабанга. Однако, как выполняются методы прерывания, может зависеть от типа выражения, переданного оператору await.

В большинстве случаев вы будете использовать await для выражения типа Task. Выполнение задачи await шаблона является «умным», поскольку оно отбрасывает SynchronizationContext, что в основном приводит к следующему:

  1. Если поток, входящий в await, включен поток потока сообщений диспетчера или WinForms, он гарантирует, что куски асинхронного метода выполняются как часть обработки очереди сообщений.
  2. Если поток, входящий в await, находится в потоке пула потоков, то оставшиеся куски асинхронного метода встречаются в любом месте пула потоков.

Вот почему вы, вероятно, сталкиваетесь с проблемами - реализация метода асинхронного тестирования пытается запустить остальные на диспетчере - даже если он приостановлен.

.... резервное копирование! ....

Я должен задать вопрос, , почему вы пытаетесь синхронно блокировать метод async? Это может привести к тому, что метод будет вызван асинхронно. В общем случае, когда вы начинаете использовать await в методе Dispatcher или UI, вам нужно будет перевернуть весь ваш асинхронный поток пользовательского интерфейса. Например, если ваш столбец был примерно таким:

  1. [Вверх] WebRequest.GetResponse ()
  2. YourCode.HelperMethod ()
  3. YourCode .AnotherMethod ()
  4. YourCode.EventHandlerMethod ()
  5. [UI Code] .Plumbing () - код WPF или WinForms
  6. [Message Loop] - WPF или WinForms Message Loop

Затем, как только код был преобразован для использования async, вы, как правило, получите

  1. [Вверх] WebRequest.GetResponseAsync ()
  2. YourCode.HelperMethodAsync ()
  3. YourCode.AnotherMethodAsync ()
  4. YourCode.EventHandlerMethodAsync ()
  5. [UI Code] .Plumbing ( ) - WPF или WinForms Code
  6. [Message Loop] - WPF или WinForms Message Loop

Фактически Ответ

Класс AsyncHelpers выше работает, потому что он ведет себя как вложенный цикл сообщений, но он устанавливает свой собственный параллельный механизм для диспетчера, вместо того, чтобы пытаться выполнить сам Диспетчер. Это один из способов решения вашей проблемы.

Другим обходным решением является выполнение вашего асинхронного метода в потоке threadpool, а затем дождаться его завершения. Делать это легко - вы можете сделать это со следующим фрагментом:

var customerList = TaskEx.RunEx(GetCustomers).Result;

Конечным API будет Task.Run (...), но с CTP вам понадобятся суффиксы Ex ( здесь ).

35
ответ дан Community 17 August 2018 в 22:42
поделиться
  • 1
    +1 для подробного объяснения, однако TaskEx.RunEx(GetCustomers).Result зависает приложение, когда оно запускается в приостановленном потоке диспетчера. Кроме того, метод GetCustomers () обычно запускается async, однако в одной ситуации он должен запускаться синхронно, поэтому я искал способ сделать это, не создавая версию метода синхронизации. – Rachel 25 February 2011 в 15:39
  • 2
    +1 для ", почему вы пытаетесь синхронно блокировать метод async? & Quot; Всегда есть способ правильно использовать методы async; конечно, следует избегать вложенных циклов. – Stephen Cleary 5 September 2013 в 16:59
  • 3
    Стивен, есть еще один очень похожий qestion , который вы также предоставили удивительный ответ. Считаете ли вы, что один из них может быть закрыт как дубликат или, возможно, запрос на объединение или вывести мета на первый (поскольку каждый q имеет ~ 200K просмотров 200+ голосов)? Предложения? – Alexei Levenkov 5 February 2017 в 09:05
  • 4
    @AlexeiLevenkov: Я не считаю, что это так, по нескольким причинам: 1) Ответ на связанный вопрос довольно устарел. 2) Я написал целую статью по этому вопросу , которая, по моему мнению, более полная, чем любая существующая SO Q / A. 3) Принятый ответ на этот вопрос популярный extreme . 4) Я против яростно против этого принятого ответа. Таким образом, закрытие этого, как дурака, было бы злоупотреблением властью; закрыв это, как дурак этого (или слияния), еще более увеличит опасный ответ. Я позволил, и оставим это сообществу. – Stephen Cleary 5 February 2017 в 15:15
  • 5
    ОК. Я подумаю о том, чтобы поднять его на мета, чем в некотором роде. – Alexei Levenkov 6 February 2017 в 19:14
  • 6
    Этот ответ проделал долгий путь над моей головой. «Использовать асинхронный путь вниз» вызывает путаницу в связи с тем, что явно невозможно следовать. Программа с методом async Main() не компилируется; в какой-то момент вы получили , чтобы преодолеть разрыв между мирами синхронизации и асинхронного программирования. Это не очень редкая ситуация с [i0] очень ", это необходимо буквально каждой программе, вызывающей метод async. Нет возможности не выполнять & quot; делать sync-over-async & quot; , просто вариант для того, чтобы отключить эту нагрузку до вызывающего метода вместо того, чтобы занять его в том, который вы сейчас пишете. – Mark Amery 13 June 2017 в 13:20
  • 7
    Отлично. Я собираюсь поместить async на все методы в моем приложении. И это много. Разве это не может быть по умолчанию? – ygoe 13 December 2017 в 17:53

Этот ответ предназначен для всех, кто использует WPF для .NET 4.5.

Если вы попытаетесь выполнить Task.Run() в потоке GUI, тогда task.Wait() будет вешать бесконечно, если вы не имеют ключевое слово async в определении вашей функции.

Этот метод расширения решает проблему, проверяя, есть ли мы в потоке графического интерфейса пользователя, и если да, выполняем задачу в потоке диспетчера WPF.

Этот класс может действовать как клей между миром async / await и неасинхронным / ожидающим миром в ситуациях, когда это неизбежно, например свойства MVVM или зависимости от других API, которые не используют async / ожидают.

/// <summary>
///     Intent: runs an async/await task synchronously. Designed for use with WPF.
///     Normally, under WPF, if task.Wait() is executed on the GUI thread without async
///     in the function signature, it will hang with a threading deadlock, this class 
///     solves that problem.
/// </summary>
public static class TaskHelper
{
    public static void MyRunTaskSynchronously(this Task task)
    {
        if (MyIfWpfDispatcherThread)
        {
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E99213. Task did not run to completion.");
            }
        }
        else
        {
            task.Wait();
            if (task.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E33213. Task did not run to completion.");
            }
        }
    }

    public static T MyRunTaskSynchronously<T>(this Task<T> task)
    {       
        if (MyIfWpfDispatcherThread)
        {
            T res = default(T);
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E89213. Task did not run to completion.");
            }
            return res;
        }
        else
        {
            T res = default(T);
            var result = Task.Run(async () => res = await task);
            result.Wait();
            if (result.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E12823. Task did not run to completion.");
            }
            return res;
        }
    }

    /// <summary>
    ///     If the task is running on the WPF dispatcher thread.
    /// </summary>
    public static bool MyIfWpfDispatcherThread
    {
        get
        {
            return Application.Current.Dispatcher.CheckAccess();
        }
    }
}
3
ответ дан Contango 17 August 2018 в 22:42
поделиться

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

Task<Customer> task = GetCustomers();
task.RunSynchronously();

Изменить:

Вы говорите, что получаете исключение. Пожалуйста, напишите более подробную информацию, включая трассировку стека. Mono содержит следующий тестовый пример:

[Test]
public void ExecuteSynchronouslyTest ()
{
        var val = 0;
        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
        t.RunSynchronously ();

        Assert.AreEqual (1, val);
}

Проверьте, работает ли это для вас. Если это не так, но очень маловероятно, у вас может быть некоторая нечетная сборка Async CTP. Если это сработает, вам может потребоваться изучить, что именно генерирует компилятор, и как Task экземпляр отличается от этого образца.

Редактировать # 2:

Я проверил с Reflector, что описанное вами исключение возникает, когда m_action - null. Это странно, но я не эксперт в Async CTP. Как я уже сказал, вы должны декомпилировать свой код и посмотреть, как именно создается Task, каким образом его m_action является null.


P.S. В чем дело с случайными сокращениями? Учиться разрабатывать?

8
ответ дан Dan Abramov 17 August 2018 в 22:42
поделиться
  • 1
    Я скорректировал свой вопрос, чтобы сделать код, который я попытался сделать более ясным. RunSynchronously возвращает ошибку RunSynchronously may not be called on a task unbound to a delegate. Google не помогает, поскольку все результаты для китайского ... – Rachel 23 February 2011 в 19:51
  • 2
    Я отредактировал свой ответ. – Dan Abramov 23 February 2011 в 20:02
  • 3
    Я считаю, что разница в том, что я не создаю задачу, а затем попытаюсь ее запустить. Вместо этого задача создается методом async, когда используется ключевое слово await. Исключение, опубликованное в моем предыдущем комментарии, является исключением, которое я получаю, хотя это одно из немногих, что я не могу Google и найти причину или разрешение. – Rachel 23 February 2011 в 20:40
  • 4
    async и async ключевые слова - не что иное, как синтаксический сахар. Компилятор создает код для создания Task<Customer> в GetCustomers(), так что я бы посмотрел первым. Что касается исключения, вы отправили только сообщение исключения, которое бесполезно без типа исключения и трассировки стека. Вызовите метод исключения ToString() и пост-вывод в вопросе. – Dan Abramov 23 February 2011 в 20:44
  • 5
    @gaearon Я думаю, что у вас были downvotes, потому что ваш пост не применим к вопросу. Обсуждаются методы асинхронного ожидания, а не простые методы возврата задачи. Более того, на мой взгляд, механизм асинхронного ожидания - это синтаксический сахар, но не настолько тривиальный - есть продолжение, захват контекста, возобновление локального контекста, расширенная обработка локальных исключений и многое другое. Затем вы не должны вызывать метод RunSynchronously по результату метода async, потому что по определению асинхронный метод должен возвращать задачу, которая в настоящее время по крайней мере запланирована, и несколько раз находится в рабочем состоянии. – sgnsajgon 15 September 2014 в 20:01

Это работает для меня

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    public static class AsyncHelper
    {
        private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

        public static void RunSync(Func<Task> func)
        {
            _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }

        public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        {
            return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }
    }

    class SomeClass
    {
        public async Task<object> LoginAsync(object loginInfo)
        {
            return await Task.FromResult(0);
        }
        public object Login(object loginInfo)
        {
            return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
            //return this.LoginAsync(loginInfo).Result.Content;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var someClass = new SomeClass();

            Console.WriteLine(someClass.Login(1));
            Console.ReadLine();
        }
    }
}
-1
ответ дан Dan Nguyen 17 August 2018 в 22:42
поделиться

Почему бы не создать такой вызов, как:

Service.GetCustomers();

, который не является асинхронным.

7
ответ дан Daniel A. White 17 August 2018 в 22:42
поделиться
  • 1
    Это будет то, что я делаю, если я не смогу это сделать ... создайте версию Sync в дополнение к версии Async – Rachel 23 February 2011 в 19:49

Я думаю, что следующий вспомогательный метод также может решить проблему.

private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
    {
        TResult result = default(TResult);
        var autoResetEvent = new AutoResetEvent(false);

        Task.Run(async () =>
        {
            try
            {
                result = await func();
            }
            catch (Exception exc)
            {
                mErrorLogger.LogError(exc.ToString());
            }
            finally
            {
                autoResetEvent.Set();
            }
        });
        autoResetEvent.WaitOne();

        return result;
    }

Может использоваться следующим образом:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
1
ответ дан donttellya 17 August 2018 в 22:42
поделиться
  • 1
    Пожалуйста, объясните голосование – donttellya 21 March 2016 в 09:22
  • 2
    ... Меня все еще интересует, почему этот ответ был проголосован? – donttellya 22 April 2016 в 08:18
  • 3
    Это не правда, «синхронно». Вы создаете два потока и ждете в первых результатах других. – tmt 18 May 2016 в 14:02
  • 4
    и все в стороне, это очень плохая идея. – Dan Pantry 13 September 2016 в 10:22
  • 5
    Я просто написал почти идентичный код (по одной строке), но вместо этого использовал SemaphoreSlim вместо события автоматического перезапуска. Жаль, что я не видел этого раньше. Я нахожу этот подход для предотвращения взаимоблокировок и сохраняю ваш асинхронный код таким же, как в истинных асинхронных сценариях. Не совсем уверен, почему это плохая идея. Кажется намного чище, чем другие подходы, которые я видел выше. – tmrog 20 June 2017 в 12:12

Я столкнулся с этим несколько раз, в основном в модульном тестировании или в разработке служб Windows. В настоящее время я всегда использую эту функцию:

        var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
        {
            Trace.WriteLine("Task runSync Start");
            await TaskEx.Delay(2000); // Simulates a method that returns a task and
                                      // inside it is possible that there
                                      // async keywords or anothers tasks
            Trace.WriteLine("Task runSync Completed");
        })).Unwrap();
        Trace.WriteLine("Before runSync Wait");
        runSync.Wait();
        Trace.WriteLine("After runSync Waited");

Это просто, легко, и у меня не было проблем.

15
ответ дан J. Lennon 17 August 2018 в 22:42
поделиться

Удивленный никто не упомянул об этом:

public Task<int> BlahAsync()
{
    // ...
}

int result = BlahAsync().GetAwaiter().GetResult();

Не так хорошо, как некоторые из других методов здесь, но он имеет следующие преимущества:

  • )
  • не будет содержать никаких исключений, которые были выбраны в AggregateException (например Result)
  • работает как для Task, так и для Task<T> ( попробуйте сами! )

Кроме того, поскольку GetAwaiter утиный, это должно работать для любого объекта, который возвращается из async (например, ConfiguredAwaitable или YieldAwaitable), а не только Задачи.


edit: Обратите внимание, что этот подход (или использование .Result) возможен в тупик, если вы не убедитесь добавлять .ConfigureAwait(false) каждый раз, когда вы ожидаете, для всех асинхронных методов, которые могут быть достигнуты с BlahAsync() (а не только с теми, которые он вызывает напрямую). Объяснение .

// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
                                        // all its descendants use ConfigureAwait(false)
                                        // too. Then you can be sure that
                                        // BlahAsync().GetAwaiter().GetResult()
                                        // won't deadlock.

Если вы слишком ленивы, чтобы добавить .ConfigureAwait(false) повсюду, и вы не заботитесь о производительности, вы также можете сделать

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
76
ответ дан James Ko 17 August 2018 в 22:42
поделиться
  • 1
    Работает для меня для простых вещей. Кроме того, если метод возвращает IAsyncOperation, мне пришлось сначала преобразовать его в задачу: BlahAsync (). AsTask (). GetAwaiter (). GetResult (); – Lee McPherson 7 April 2016 в 05:22
  • 2
    Это вызвало тупик внутри веб-метода asmx. Тем не менее, завершение вызова метода в Task.Run () заставило его работать: Task.Run (() = & gt; BlahAsync ()). GetAwaiter (). GetResult () – Augusto Barreto 13 December 2017 в 15:30
  • 3
    Мне нравится этот подход лучше всего синтаксически, потому что он не связан с лямбдами. – dythim 30 January 2018 в 03:19
  • 4
    Пожалуйста, не редактируйте ответы других людей, чтобы вставить ссылку в вашу собственную. Если вы считаете, что ваш ответ лучше, оставьте его вместо комментария. – Rachel 1 March 2018 в 22:48
  • 5
    Должно быть решение вопроса, поскольку оно решает проблему, минимальный пользовательский код и ответ прекрасно объясняют ключевые моменты. – Ghen 26 March 2018 в 01:48

Вы можете использовать CoRoutines . См. Реализацию Caliburn.Micro . У меня есть пользовательская реализация здесь .

0
ответ дан Jone Polvora 17 August 2018 в 22:42
поделиться
  • 1
    Пожалуйста, не могли бы вы привести какой-нибудь пример? – sgnsajgon 15 September 2014 в 19:49
    private int GetSync()
    {
        try
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            int result = null;

            Parallel.Invoke(async () =>
            {
                result = await SomeCalcAsync(5+5);
                mre.Set();
            });

            mre.WaitOne();
            return result;
        }
        catch (Exception)
        {
            return null;
        }
    }
-4
ответ дан ksemenenko 17 August 2018 в 22:42
поделиться

использовать ниже код snip

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
5
ответ дан Mahesh 17 August 2018 в 22:42
поделиться

Попробуйте использовать следующий код для меня:

public async void TaskSearchOnTaskList (SearchModel searchModel)
{
    try
    {
        List<EventsTasksModel> taskSearchList = await Task.Run(
            () => MakeasyncSearchRequest(searchModel),
            cancelTaskSearchToken.Token);

        if (cancelTaskSearchToken.IsCancellationRequested
                || string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
        {
            return;
        }

        if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
        {
            RunOnUiThread(() => {
                textViewNoMembers.Visibility = ViewStates.Visible;                  
                taskListView.Visibility = ViewStates.Gone;
            });

            taskSearchRecureList = null;

            return;
        }
        else
        {
            taskSearchRecureList = TaskFooterServiceLayer
                                       .GetRecurringEvent(taskSearchList);

            this.SetOnAdapter(taskSearchRecureList);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
    }
}
-9
ответ дан Massimiliano Kraus 17 August 2018 в 22:42
поделиться

Гораздо проще запустить задачу в пуле потоков, а не пытаться обмануть планировщика для его синхронного запуска. Таким образом, вы можете быть уверены, что это не затормозит. Эффект зависит от контекстного переключателя.

Task<MyResult> DoSomethingAsync() { ... }

// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());

// Will block until the task is completed...
MyResult result = task.Result; 
64
ответ дан Michael L Perry 17 August 2018 в 22:42
поделиться
  • 1
    И если задача недействительна (без результата)? – J. Lennon 7 October 2013 в 00:17
  • 2
    Затем вы вызываете task.Wait (). Тип данных - это просто «Задача». – Michael L Perry 9 April 2014 в 15:51
  • 3
    Предположим, что DoSomethingAsync () - это длительный async-метод в целом (внутренне он ожидает долговременную задачу), но он быстро возвращает управление потоком своему вызывающему абоненту, поэтому работа аргумента лямбда заканчивается также быстро. Результат Tusk.Run () может Задача & lt; Задача & gt; или Задача & lt; Задача & gt; & gt; & gt; , так что вы ожидаете результата внешней задачи, которая быстро завершается , но внутренняя задача (из-за ожидания длительной работы в асинхронном методе) все еще работает. Выводы заключаются в том, что мы, вероятно, должны использовать подход Unwrap () (как это было сделано в post @ J.Lennon) для достижения синхронного поведения асинхронного метода. – sgnsajgon 13 September 2014 в 10:33
  • 4
    @sgnsajgon. Вы ошибаетесь. Task.Run отличается от Task.Factory.StartNew тем, что он автоматически разворачивает результат. См. в этой статье . – ZunTzu 2 November 2015 в 10:36
  • 5
    Могу ли я просто написать Task.Run(DoSomethingAsync)? Это удаляет один уровень делегатов. – ygoe 13 December 2017 в 17:36

Просто вызов .Result; или .Wait() - это риск блокировок, о котором многие говорили в комментариях. Поскольку большинство из нас, как oneliners, вы можете использовать их для .Net 4.5<

Приобретение значения с помощью метода async:

var result = Task.Run(() => asyncGetValue()).Result;

Синхронно вызывать метод асинхронизации

Task.Run(() => asyncMethod()).Wait();

Никаких проблем с блокировкой не произойдет из-за использования Task.Run.

Источник:

https://stackoverflow.com/a/32429753/3850405

3
ответ дан Ogglas 17 August 2018 в 22:42
поделиться

Самый простой способ, который я нашел для запуска задачи синхронно и без блокировки потока пользовательского интерфейса, - использовать RunSynchronously () как:

Task t = new Task(() => 
{ 
   //.... YOUR CODE ....
});
t.RunSynchronously();

В моем случае у меня есть событие, которое срабатывает, когда что-то происходит. Я не знаю, сколько раз это произойдет. Таким образом, я использую код выше в моем случае, поэтому всякий раз, когда он срабатывает, он создает задачу. Задачи выполняются синхронно, и это отлично работает для меня. Я был просто удивлен, что мне потребовалось столько времени, чтобы выяснить, насколько это просто. Обычно рекомендации намного сложнее и подвержены ошибкам. Это было просто и чисто.

16
ответ дан pixel 17 August 2018 в 22:42
поделиться

Рекомендовать этому ответу три года. Я написал его, основываясь главным образом на опыте с .Net 4.0 и очень мало с 4.5, особенно с async-await. Вообще говоря, это приятное простое решение, но иногда это ломает ситуацию. Пожалуйста, прочитайте обсуждение в комментариях.

.Net 4.5

Просто используйте это:

// For Task<T>: will block until the task is completed...
var result = task.Result; 

// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();

См.: TaskAwaiter , Task.Result , Task.RunSynchronously


.Net 4.0

Используйте это:

var x = (IAsyncResult)task;
task.Start();

x.AsyncWaitHandle.WaitOne();

... или это:

task.Start();
task.Wait();
283
ответ дан Rachel 17 August 2018 в 22:42
поделиться
  • 1
    Для некоторого фона, как это работает, Стивен Туб (Mr Parallel) написал серию сообщений об этом. Часть 1 Часть 2 Часть 3 – Cameron MacFarland 8 February 2013 в 18:29
  • 2
    Я обновил код Джона для работы без обертывания задач в lambdas: github.com/tejacques/AsyncBridge . По сути, вы работаете с асинхронными блоками с помощью инструкции using. Все внутри блока использования происходит асинхронно, с ожиданием в конце. Недостатком является то, что вам нужно развернуть задачу самостоятельно в обратном вызове, но она по-прежнему довольно элегантна, особенно если вам нужно сразу вызвать несколько асинхронных функций. – Tom Jacques 25 June 2013 в 20:47
  • 3
    .Result может создать тупик в определенных сценариях – Jordy Langen 22 August 2013 в 20:17
  • 4
    Result может легко вызвать тупик в коде async , как я описываю в своем блоге. – Stephen Cleary 5 September 2013 в 16:58
  • 5
    @StephenCleary Я прочитал ваше сообщение и сам пробовал. Я честно считаю, что кто-то из Microsoft был действительно пьян ... Это та же проблема, что и winforms и фоновые темы .... – AK_ 15 October 2013 в 17:36
  • 6
    @StephenCleary Хотя я, как правило, согласен с вами в том, что код должен быть асинхронным до конца, иногда вы оказываетесь в ситуации невозможности, когда один имеет , чтобы заставить его использовать синхронный вызов. В основном, моя ситуация в том, что весь мой код доступа к данным имеет асинхронный характер. Мне нужно было создать карту сайта на основе карты сайта, а сторонней библиотекой, которую я использовал, была MvcSitemap. Теперь, когда он расширяет его с помощью базового класса DynamicNodeProviderBase, нельзя объявить его как метод async. Либо мне пришлось заменить новую библиотеку, либо просто вызвать синхронный режим. – justin.lovell 23 January 2014 в 08:56
  • 7
    @ justin.lovell: Да, ограничения библиотеки могут заставить нас ввести хаки, по крайней мере, до тех пор, пока библиотека не будет обновлена. Похоже, что MvcSitemap - это такая ситуация, когда требуется взломать (фильтры MVC и дочерние действия тоже); Я просто отговариваю людей от этого вообще, потому что такие хаки слишком часто используются, когда они not необходимы. С MVC, в частности, некоторые API ASP.NET / MVC предполагают, что у них есть AspNetSynchronizationContext, поэтому этот конкретный взлом не будет работать, если вы вызываете эти API. – Stephen Cleary 23 January 2014 в 14:33
  • 8
    Вопрос касается задачи, возвращаемой методом async. Такая задача может быть запущена, выполнена или отменена, поэтому использование метода Task.RunSynchronously может привести к InvalidOperationException . См. Страницу MSDN: Task.RunSynchronously Method . Кроме того, эта задача, вероятно, создается методами Task.Factory.StartNew или Task.Run (внутри асинхронного метода), поэтому повторить попытку начать снова. Некоторые условия гонки могут возникать во время выполнения. В руке Task.Wait и Задача. Результат может привести к тупиковой ситуации. – sgnsajgon 13 September 2014 в 11:02
  • 9
    Run Synchronously работал для меня ... Я не знаю, если я что-то упустил, но это кажется предпочтительным для ужасов отмеченного ответа - я просто искал способ отключения асинхронного тестирования кода, который там останавливался ui от подвешивания – Jonny Leeds 25 September 2014 в 12:17
  • 10
    Этот код не будет работать. Если он вызывается из потока пула, он может вызвать тупик, зависящий от потока. Ваш вызывающий абонент заблокирует ожидание завершения операции, что может не произойти, если он исчерпал пул потоков. См. в этой статье . – ZunTzu 27 October 2015 в 19:30

Или вы могли бы просто пойти с:

customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;

Для этого для компиляции убедитесь, что вы ссылаетесь на сборку:

System.Net.Http.Formatting
-4
ответ дан tinlyx 17 August 2018 в 22:42
поделиться

В wp8:

Оберните его:

Task GetCustomersSynchronously()
{
    Task t = new Task(async () =>
    {
        myCustomers = await GetCustomers();
    }
    t.RunSynchronously();
}

Вызовите его:

GetCustomersSynchronously();
-3
ответ дан user2113284 17 August 2018 в 22:42
поделиться
  • 1
    Нет, это не работает, потому что задача не ждет делегата от конструктора (его делегат, а не задача ..) – Rico Suter 29 July 2013 в 12:09

Я нашел этот код в компоненте Microsoft.AspNet.Identity.Core, и он работает.

private static readonly TaskFactory _myTaskFactory = new 
     TaskFactory(CancellationToken.None, TaskCreationOptions.None, 
     TaskContinuationOptions.None, TaskScheduler.Default);

// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
    CultureInfo cultureUi = CultureInfo.CurrentUICulture;
    CultureInfo culture = CultureInfo.CurrentCulture;
    return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = cultureUi;
        return func();
    }).Unwrap<TResult>().GetAwaiter().GetResult();
}
10
ответ дан wenhx 17 August 2018 в 22:42
поделиться
36
ответ дан Community 6 September 2018 в 14:30
поделиться
39
ответ дан Community 29 October 2018 в 20:49
поделиться
1
ответ дан Liang 29 October 2018 в 20:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: