Jak uruchomić metodę Zadań asynchronicznych synchronicznie?

Uczę się asynchronicznego / oczekującego i natrafiłem na sytuację, w której muszę synchronicznie wywołać metodę asynchroniczną. Jak mogę to zrobić?

Metoda asynchroniczna:

public async Task<Customers> GetCustomers()
{
    return await Service.GetCustomersAsync();
}

Normalne użycie:

public async void GetCustomers()
{
    customerList = await GetCustomers();
}

Próbowałem użyć:

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

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

Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)

Próbowałem również sugestii z tutaj , jednak nie działa, gdy dyspozytor jest w stanie zawieszonym.

public static void WaitWithPumping(this Task task) 
{
        if (task == null) throw new ArgumentNullException(“task”);
        var nestedFrame = new DispatcherFrame();
        task.ContinueWith(_ => nestedFrame.Continue = false);
        Dispatcher.PushFrame(nestedFrame);
        task.Wait();
}

Oto wyjątek i stos trace z wywołania RunSynchronously:

System.InvalidOperationException

Message : RunSynchronously nie może być wywoływane w zadaniu niezwiązanym z delegatem.

InnerException : null

Źródło : mscorlib

StackTrace :

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
Author: Hamid Pourjam, 0000-00-00

23 answers

Oto obejście, które działa we wszystkich sprawach (w tym zawieszonych dyspozytorów). To nie jest mój kod i nadal pracuję, aby go w pełni zrozumieć, ale to działa.

Można go wywołać używając:

customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());

Kod pochodzi z tutaj

public static class AsyncHelpers
{
    /// <summary>
    /// Execute's an async Task<T> method which has a void return value synchronously
    /// </summary>
    /// <param name="task">Task<T> method to execute</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        synch.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Execute's an async Task<T> method which has a T return type synchronously
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        T ret = default(T);
        synch.Post(async _ =>
        {
            try
            {
                ret = await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();
        SynchronizationContext.SetSynchronizationContext(oldContext);
        return ret;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private bool done;
        public Exception InnerException { get; set; }
        readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();

        public override void Send(SendOrPostCallback d, object state)
        {
            throw new NotSupportedException("We cannot send to our same thread");
        }

        public override void Post(SendOrPostCallback d, object state)
        {
            lock (items)
            {
                items.Enqueue(Tuple.Create(d, state));
            }
            workItemsWaiting.Set();
        }

        public void EndMessageLoop()
        {
            Post(_ => done = true, null);
        }

        public void BeginMessageLoop()
        {
            while (!done)
            {
                Tuple<SendOrPostCallback, object> task = null;
                lock (items)
                {
                    if (items.Count > 0)
                    {
                        task = items.Dequeue();
                    }
                }
                if (task != null)
                {
                    task.Item1(task.Item2);
                    if (InnerException != null) // the method threw an exeption
                    {
                        throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
                    }
                }
                else
                {
                    workItemsWaiting.WaitOne();
                }
            }
        }

        public override SynchronizationContext CreateCopy()
        {
            return this;
        }
    }
}
 391
Author: Rachel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-01 21:47:21

Uwaga ta odpowiedź ma trzy lata. Napisałem go głównie na podstawie doświadczeń z. Net 4.0, a bardzo niewiele z 4.5 zwłaszcza z async-await. Ogólnie rzecz biorąc, jest to ładne proste rozwiązanie, ale czasami psuje rzeczy. Proszę o zapoznanie się z dyskusją w komentarzach.

. Net 4.5

Po prostu użyj tego:

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

Zobacz: TaskAwaiter , zadanie.Wynik , zadanie.RunSynchronously


. Net 4.0

Użycie to:

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

x.AsyncWaitHandle.WaitOne();

...albo to:

task.Start();
task.Wait();
 290
Author: AK_,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-01 21:47:48

Dziwi mnie, że nikt o tym nie wspomniał:

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

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

Nie tak ładny jak niektóre z innych metod tutaj, ale ma następujące zalety: {]}

  • Nie połyka WYJĄTKÓW (jak Wait)
  • Nie zawija żadnych WYJĄTKÓW rzuconych w AggregateException (Jak Result)
  • Działa zarówno dla Task jak i Task<T> (wypróbuj sam!)

Również, ponieważ {[8] } jest pisane kaczką, powinno to działać dla każdego obiektu, który jest zwracany z metody asynchronicznej (jak ConfiguredAwaitable lub YieldAwaitable), nie tylko zadania.


Edit: zauważ, że jest możliwe, aby to podejście (lub użycie .Result) zablokować, chyba że upewnij się, że dodajesz .ConfigureAwait(false) za każdym razem, gdy czekasz, dla wszystkich metod asynchronicznych, które można uzyskać z BlahAsync() (nie tylko tych, które wywołują bezpośrednio). Wyjaśnienie .

// 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.

Jeśli jesteś zbyt leniwy, aby dodać .ConfigureAwait(false) wszędzie, a nie zależy ci na wydajności, możesz alternatywnie zrobić

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
 81
Author: James Ko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-24 04:32:09

O wiele prostsze jest uruchamianie zadania na puli wątków, zamiast próbować oszukać terminarza, aby uruchomił je synchronicznie. W ten sposób możesz być pewien, że nie będzie impasu. Wpływ na wydajność ma przełącznik kontekstowy.

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; 
 65
Author: Michael L Perry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-13 18:50:54

Uczę się asynchronicznego / oczekującego i natrafiłem na sytuację, w której muszę synchronicznie wywołać metodę asynchroniczną. Jak mogę to zrobić?

Najlepszą odpowiedzią jest nie wiesz , A szczegóły zależą od tego, jaka jest "sytuacja".

Czy to getter/setter nieruchomości? W większości przypadków lepiej mieć metody asynchroniczne niż"właściwości asynchroniczne". (Aby uzyskać więcej informacji, zobacz mój wpis na blogu o właściwościach asynchronicznych ).

Czy jest to aplikacja MVVM i chcesz zrobić asynchroniczne powiązanie danych? Więc użyj czegoś takiego jak mój NotifyTask, jak opisano w moim artykule MSDN na temat asynchronicznego powiązania danych.

Czy to Konstruktor? Następnie prawdopodobnie chcesz rozważyć asynchroniczną metodę fabryczną. (Więcej informacji można znaleźć w moim wpisie na blogu o konstruktorach asynchronicznych ).

Prawie zawsze jest lepsza odpowiedź niż synchronizacja nad asynchronizacją.

Jeśli nie jest to możliwe dla twojej sytuacji (i wiesz o tym pytając pytanie tutaj opisujące sytuację ), wtedy polecam po prostu użycie kodu synchronicznego. Asynchronizacja jest najlepsza; synchronizacja jest na drugim miejscu. Synchronizacja przez asynchronizację nie jest zalecana.

Jest jednak kilka sytuacji, w których synchronizacja nad asynchronizacją jest konieczna. W szczególności, jesteś ograniczony przez kod wywołujący, dzięki czemu masz być synchronizowany (i absolutnie nie masz sposobu na ponowne przemyślenie lub zmianę struktury kodu, aby umożliwić asynchroniczną), i możesz mieć aby wywołać kod asynchroniczny. To jest bardzo rzadka sytuacja, ale pojawia się od czasu do czasu.

W takim przypadku trzeba by użyć jednego z hacków opisanych w moim artykule na temat brownfield async development , a konkretnie:

  • blokowanie (np. GetAwaiter().GetResult()). Zauważ, że może to powodować blokady (jak opisuję na moim blogu).
  • uruchamianie kodu na wątku puli wątków (np. Task.Run(..).GetAwaiter().GetResult()). Zauważ, że będzie to działać tylko wtedy, gdy kod asynchroniczny może być uruchamiany na wątku puli wątków (tzn. nie jest zależny od interfejsu użytkownika lub ASP.NET kontekst).
  • zagnieżdżone pętle wiadomości. Zauważ, że będzie to działać tylko wtedy, gdy kod asynchroniczny przyjmuje tylko kontekst jednowątkowy, a nie specyficzny typ kontekstu (wiele interfejsów i ASP.NET kod oczekuje konkretnego kontekstu).

Zagnieżdżone pętle wiadomości są najniebezpieczniejsze ze wszystkich hacków, ponieważ powodują ponowne wejście . Ponowne wejście jest niezwykle trudne do rozumowania about, and (IMO) jest przyczyną większości błędów aplikacji w systemie Windows. W szczególności, jeśli jesteś w wątku UI i blokujesz kolejkę roboczą (czekając na zakończenie pracy asynchronicznej), CLR faktycznie pompuje za Ciebie wiadomość - faktycznie obsłuży niektóre wiadomości Win32 z twojego kodu. Oh, i nie masz pojęcia, które wiadomości-kiedy Chris Brumme mówi "czy nie byłoby wspaniale wiedzieć dokładnie, co zostanie pompowane? Niestety pompowanie to czarna Sztuka co jest poza śmiertelnym zrozumieniem.", wtedy naprawdę nie mamy nadziei, że się dowiemy.

Więc, kiedy blokujesz tak na wątku UI, prosisz o kłopoty. Kolejny cytat cbrumme z tego samego artykułu: "od czasu do czasu klienci wewnątrz lub na zewnątrz firmy odkrywają, że pompujemy wiadomości podczas zarządzanego blokowania w wątku STA [UI]. Jest to uzasadniony problem, ponieważ wiedzą, że bardzo trudno jest napisać kod, który jest solidny w obliczu reentrancy."

Tak, tak. bardzo trudny do napisania kod, który jest solidny w obliczu reentrancy. Zagnieżdżone pętle wiadomości zmuszają cię do pisania kodu, który jest solidny w obliczu reentrancji. To dlatego przyjęta (i najbardziej upvoted) odpowiedź na to pytanie jest niezwykle niebezpieczna {33]} w praktyce.

Jeśli jesteś całkowicie poza wszystkimi innymi opcjami-nie możesz przeprojektować kodu, nie możesz przekształcić go w asynchroniczny - jesteś zmuszony przez niezmienny kod wywołujący do synchronizacji - nie można zmienić kodu do synchronizacji-nie można zablokować-nie można uruchomić kodu asynchronicznego w oddzielnym wątku - następnie i dopiero wtedy należy rozważyć włączenie reentrancy.

Jeśli znajdziesz się w tym rogu, polecam użycie czegoś takiego jak Dispatcher.PushFrame dla aplikacji WPF , zapętlanie za pomocą Application.DoEvents dla aplikacji WinForm, a dla ogólnego przypadku moje własne AsyncContext.Run.

 38
Author: Stephen Cleary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:38

To działa dobrze dla mnie

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;
    }
}
 21
Author: Clement,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-23 05:42:37

Jeśli dobrze czytam twoje pytanie-kod, który chce synchronicznego wywołania metody asynchronicznej, jest wykonywany na zawieszonym wątku dyspozytora. I chcesz rzeczywiście synchronicznie zablokować ten wątek, dopóki metoda asynchroniczna nie zostanie zakończona.

Metody asynchroniczne w C # 5 są napędzane przez efektywne rozdrabnianie metody na kawałki pod maską i zwracanie Task, które mogą śledzić całościowe zakończenie całego shabang. Jednak jak te metody mogą zależy od typu wyrażenia przekazanego do operatora await.

Przez większość czasu będziesz używać await na wyrażeniu typu Task. Implementacja wzorca await jest "inteligentna" w tym, że odwraca się do SynchronizationContext, co zasadniczo powoduje, że następuje:

  1. jeśli wątek wprowadzający await znajduje się w wątku pętli Dispatcher lub WinForms, zapewnia to, że fragmenty metody asynchronicznej wystąpią w ramach przetwarzania wiadomości Kolejka
  2. jeśli wątek wchodzący do await znajduje się na wątku puli wątków, to pozostałe fragmenty metody asynchronicznej występują w dowolnym miejscu puli wątków.

Dlatego prawdopodobnie masz problemy - implementacja metody async próbuje uruchomić resztę na Dyspozytorze-mimo że jest zawieszona.

.... wycofujemy się! ....

Muszę zadać pytanie, dlaczego próbujesz synchronicznie zablokować metodę asynchroniczną? Byłoby to sprzeczne z celem, dlaczego metoda chciała być nazywana asynchronicznie. Ogólnie rzecz biorąc, kiedy zaczniesz używać await w metodzie Dispatcher lub UI, będziesz chciał zmienić cały asynchroniczny przepływ UI. Na przykład, jeśli twój callstack był czymś w stylu:

  1. [Top] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing() - WPF lub WinForms kod
  6. [pętla Wiadomości] - WPF or WinForms Message Loop

Po przekształceniu kodu w asynchroniczny, zazwyczaj kończy się

  1. [Top] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing() - WPF lub WinForms kod
  6. [pętla Wiadomości] - WPF lub WinForms pętla wiadomości

Właściwie Odpowiadam

Powyższa Klasa AsyncHelpers działa, ponieważ zachowuje się jak zagnieżdżona wiadomość loop, ale instaluje własną mechanikę równoległą do dyspozytora, a nie próbuje wykonać na samym Dyspozytorze. To jedno rozwiązanie problemu.

Innym obejściem jest wykonanie metody asynchronicznej na wątku threadpool, a następnie oczekiwanie na jej zakończenie. Jest to łatwe - możesz to zrobić za pomocą następującego fragmentu:]}

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

Finalnym API będzie Task.Bieg(...), ale z CTP będziesz potrzebował przyrostków Ex (Wyjaśnienie tutaj ).

 18
Author: Theo Yaung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-28 23:27:00

Najprostszym sposobem, jaki znalazłem, aby uruchomić zadanie synchronicznie i bez blokowania wątku UI, jest użycie RunSynchronously () jak:

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

W moim przypadku, mam zdarzenie, które odpala, gdy coś się dzieje. Nie wiem, ile razy to nastąpi. Więc, używam kodu powyżej w moim przypadku, więc kiedy to odpala, tworzy zadanie. Zadania są wykonywane synchronicznie i to działa świetnie dla mnie. Byłem po prostu zaskoczony, że zajęło mi tak długo, aby dowiedzieć się o tym, biorąc pod uwagę, jak proste to jest. Zwykle, zalecenia są znacznie bardziej złożone i podatne na błędy. To było to jest proste i czyste.

 16
Author: pixel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-25 23:22:45

Miałem do czynienia z tym kilka razy, głównie w testach jednostkowych lub w rozwoju usług windows. Obecnie zawsze używam tej funkcji:

        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");
To proste, łatwe i nie miałem żadnych problemów.
 15
Author: J. Lennon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-06 13:32:38

Znalazłem ten kod w Microsofcie.AspNet.Tożsamość.Podstawowy komponent i działa.

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();
}
 12
Author: wenhx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-02 02:23:57

Mała notka - takie podejście:

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

Działa dla WinRT.

Pozwól mi wyjaśnić:

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

Ponadto to podejście działa tylko dla rozwiązań Windows Store!

Uwaga: ten sposób nie jest bezpieczny, jeśli wywołasz swoją metodę wewnątrz innej metody asynchronicznej (zgodnie z komentarzami @Servy)

 9
Author: RredCat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-19 15:53:06

W Twoim kodzie, twój pierwszy czeka na wykonanie zadania, ale jeszcze go nie uruchomiłeś, więc czeka w nieskończoność. Spróbuj tego:

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

Edit:

Mówisz, że dostajesz wyjątek. Proszę zamieścić więcej szczegółów, w tym śledzenie stosu.
Mono zawiera następujący przypadek testowy:
[Test]
public void ExecuteSynchronouslyTest ()
{
        var val = 0;
        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
        t.RunSynchronously ();

        Assert.AreEqual (1, val);
}

Sprawdź, czy to działa dla Ciebie. Jeśli tak nie jest, choć bardzo mało prawdopodobne, możesz mieć dziwną budowę asynchronicznego CTP. Jeśli to zadziała, możesz sprawdzić, co dokładnie kompilator generuje i czym instancja Task różni się od tej próbki.

Edytuj #2:

Sprawdziłem za pomocą Reflector, że opisany wyjątek występuje, gdy m_action jest null. To trochę dziwne, ale nie jestem ekspertem od asynchronicznego CTP. Jak mówiłem, powinieneś zdekompilować swój kod i zobaczyć jak dokładnie Task jest tworzona instancja, jak to się stało, że jego m_action jest null.


P. S. O co chodzi z okazjonalnymi downvotami? Możesz to rozwinąć?

 8
Author: Dan Abramov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-17 13:08:48

Dlaczego nie utworzyć wywołania typu:

Service.GetCustomers();
To nie jest asynchroniczne.
 7
Author: Daniel A. White,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-23 18:20:34

Użyj poniższego kodu snip

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
 5
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-19 04:13:30

Ta odpowiedź jest przeznaczona dla każdego, kto używa WPF dla. NET 4.5.

Jeśli spróbujesz wykonać Task.Run() w wątku GUI, task.Wait() zawiesi się w nieskończoność, jeśli nie masz w definicji funkcji słowa kluczowego async.

Ta metoda rozszerzenia rozwiązuje problem, sprawdzając, czy jesteśmy w wątku GUI, a jeśli tak, uruchamiając zadanie w wątku dyspozytora WPF.

Klasa ta może działać jako klej pomiędzy światem asynchronicznym/oczekującym a światem Nie-asynchronicznym / oczekującym, w sytuacjach, gdy jest to nieuniknione, takich jak właściwości MVVM lub zależności od innych API, które nie używają asynchronicznych / oczekujących.

/// <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
Author: Contango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-13 12:51:41

Po prostu wywołanie .Result; lub .Wait() jest ryzykiem impasu, jak wielu powiedziało w komentarzach. Ponieważ większość z nas lubi onelinery, możesz ich użyć do .Net 4.5<

Pozyskiwanie wartości metodą asynchroniczną:

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

Syncronously calling an async method

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

Nie pojawią się problemy z impasami z powodu użycia Task.Run.

Źródło:

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

 3
Author: Ogglas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-25 13:49:42

Myślę, że następująca metoda pomocnicza również mogłaby rozwiązać problem.

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

Można użyć w następujący sposób:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
 1
Author: donttellya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-16 09:58:37

Możesz użyć Korutyn . Zobacz Caliburn.Implementacja Micro . Mam tutaj niestandardową implementację .

 0
Author: Jone Polvora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-01 18:20:49

This is works for me

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
Author: Dan Nguyen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 15:20:06

Na wp8:

Wrap it:

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

Nazwij to:

GetCustomersSynchronously();
 -3
Author: user2113284,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-26 23:13:36
    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
Author: ksemenenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-27 20:29:16

Lub możesz po prostu wybrać:

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

Aby to skompilować upewnij się, że odwołujesz się do rozszerzenia assembly:

System.Net.Http.Formatting
 -4
Author: user2057962,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-08 23:28:27

Spróbuj wykonać następujący kod, który działa dla mnie:

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
Author: gandhraj gayakwad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-13 16:18:53