760 Stimmen

Wie kann ich eine asynchrone Task<T>-Methode synchron ausführen?

Ich lerne über async/await, und lief in einer Situation, wo ich eine async-Methode synchron aufrufen müssen. Wie kann ich das tun?

Asynchrones Verfahren:

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

Normaler Gebrauch:

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

Ich habe die folgenden Methoden ausprobiert:

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

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

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

Ich habe auch eine Anregung von aquí Es funktioniert jedoch nicht, wenn sich der Dispatcher in einem angehaltenen Zustand befindet.

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

Hier ist die Ausnahme und der Stacktrace vom Aufruf RunSynchronously :

System.InvalidOperationException

Nachricht : RunSynchronously darf nicht für eine Aufgabe aufgerufen werden, die nicht an einen Delegaten gebunden ist.

InnerException : null

Quelle : 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()

54 Stimmen

Die beste Antwort auf die Frage "Wie kann ich eine asynchrone Methode synchron aufrufen" ist "gar nicht". Es gibt Hacks um zu versuchen, sie zum Funktionieren zu zwingen, aber sie haben alle sehr subtile Tücken. Gehen Sie stattdessen zurück und korrigieren Sie den Code, der dies "erfordert".

92 Stimmen

@Stephen Cleary Absolut zustimmen, aber manchmal seine einfach unvermeidlich, z. B. wenn Ihr Code auf einige 3rd-Party-API abhängig ist, die nicht async/await verwendet. Darüber hinaus, wenn die Bindung an WPF-Eigenschaften bei Verwendung von MVVM, seine buchstäblich unmöglich, async/await zu verwenden, da dies nicht auf Eigenschaften unterstützt wird.

5 Stimmen

@StephenCleary Nicht immer. Ich baue eine DLL, die dann in GeneXus . Es unterstützt keine async/await Schlüsselwörter, so dass ich nur synchrone Methoden verwenden muss.

15voto

pixel Punkte 8064

Der einfachste Weg, den ich gefunden habe, um Aufgabe synchron und ohne blockierenden UI-Thread auszuführen, ist die Verwendung von RunSynchronously() wie:

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

In meinem Fall habe ich ein Ereignis, das ausgelöst wird, wenn etwas passiert. Ich weiß nicht, wie oft es auftreten wird. Ich verwende also den obigen Code in meinem Ereignis, so dass jedes Mal, wenn es ausgelöst wird, eine Aufgabe erstellt wird. Die Aufgaben werden synchron ausgeführt, und für mich funktioniert das hervorragend. Ich war nur überrascht, dass ich so lange gebraucht habe, um dies herauszufinden, wenn man bedenkt, wie einfach es ist. Normalerweise sind Empfehlungen viel komplexer und fehleranfälliger. Hier war es einfach und sauber.

14voto

RredCat Punkte 4955

Nur eine kleine Anmerkung - dieser Ansatz:

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

funktioniert für WinRT.

Lassen Sie mich das erklären:

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

Außerdem funktioniert dieser Ansatz nur für Windows Store-Lösungen!

Anmerkung: Dieser Weg ist nicht thread-sicher, wenn Sie Ihre Methode innerhalb einer anderen asynchronen Methode aufrufen (laut den Kommentaren von @Servy)

10voto

Jaider Punkte 13198

HINWEIS: Ich denke, als Best Practice wird nicht empfohlen, um die Art der Aktion zu ändern, wenn es asynchron ist das Beste, was ist die Handhabung, wie es ist (async den ganzen Weg). Auf diese Weise können Sie andere Vorteile wie parallele Verarbeitung / Multi-Threading, etc. erhalten.

Da die anderen Antworten diesen Ansatz nicht verwendet haben, möchte ich ihn auch hier veröffentlichen:

var customers = GetCustomersAsync().GetAwaiter().GetResult();

7voto

Dan Abramov Punkte 252334

In Ihrem Code ist die erste warten für die Ausführung der Aufgabe, aber Sie haben sie nicht gestartet, so dass sie auf unbestimmte Zeit wartet. Versuchen Sie dies:

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

Edit :

Sie sagen, dass Sie eine Ausnahme erhalten. Bitte posten Sie mehr Details, einschließlich Stack Trace.
Mono enthält den folgenden Testfall:

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

        Assert.AreEqual (1, val);
}

Prüfen Sie, ob dies bei Ihnen funktioniert. Wenn dies nicht der Fall ist, obwohl es sehr unwahrscheinlich ist, haben Sie möglicherweise eine seltsame Version von Async CTP. Wenn es funktioniert, sollten Sie untersuchen, was genau der Compiler erzeugt und wie Task Instanziierung unterscheidet sich von diesem Beispiel.

Bearbeiten #2:

Ich habe mit Reflector überprüft, dass die von Ihnen beschriebene Ausnahme auftritt, wenn m_action es null . Das ist ein bisschen seltsam, aber ich bin kein Experte für Async CTP. Wie ich schon sagte, sollten Sie Ihren Code dekompilieren und sehen, wie genau Task instanziiert wird, wie kommt es, dass seine m_action es null .

5voto

Daniel A. White Punkte 180762

Warum nicht einen Aufruf erstellen wie:

Service.GetCustomers();

die nicht asynchron ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X