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.
0 Stimmen
@DineiA.Rockenbach: Drei bessere Alternativen fallen mir sofort ein: 1) GeneXus soll Unterstützung für async-Methoden hinzufügen; 2) Implementieren eines Callbacks/Ereignisses statt der Verwendung von
Task
(oft für sprachübergreifende Interoperabilität erforderlich); 3) Verwenden Sie ausschließlich synchronen Code, so dass Sync-over-Async gar nicht erst zur Sprache kommt. Jede dieser Möglichkeiten ist eine bessere Alternative zur Verwendung zweifelhafter Hacks.7 Stimmen
@StephenCleary 1) GeneXus ist ein 3rd pt-Tool und ich habe keinen Zugang zu seinem Quellcode; 2) GeneXus hat nicht einmal Implementierungen von "Funktionen", so dass ich nicht erkennen kann, wie ich einen "Callback" mit dieser Art von Ding implementieren könnte. Sicherlich wäre es ein schwierigerer Workaround als die Verwendung von
Task
synchron; 3) ich integriere GeneXus mit MongoDB C#-Treiber , die einige Methoden nur asynchron zur Verfügung stellen9 Stimmen
@StephenCleary Das ist alles schöne Theorie, aber "nicht tun" hat das inhärente Problem mit ihm, dass es "nicht funktioniert". C# verbietet mir aktiv die Verwendung von
await
innerhalb synchronisierter Blöcke. Soll ich Microsoft auffordern, seine Sprache zu ändern? Oder sollte ich die Synchronisierung aufgeben und die verkorksten Datenstrukturen akzeptieren?async
ist der Krebs, nicht so sehr die GPL. Wenn man es einmal hat, wird man es nicht mehr los.2 Stimmen
@ygoe: Verwenden Sie eine async-kompatible Sperre, wie z.B.
SemaphoreSlim
.3 Stimmen
Das Fehlen der größten Namen auf StackOverflow selbst Ende 2016 deutet stark darauf hin, dass es immer noch keine einfache Methode gibt, um dies zu tun. BTW. Ich frage mich, warum es keine
runsync
Betreiber.1 Stimmen
Mögliches Duplikat von Wie ruft man eine asynchrone Methode von einer synchronen Methode in C# auf?
0 Stimmen
Verknüpfung einer zugehörige Frage/Antwort wie Sie dies in einem UI-Thread tun können.
0 Stimmen
@StephenCleary warum .net nicht so etwas wie ".RunAsSynchronous()"-Methode, die wir auf Task-Typ aufrufen können? diese Art von Methode würde einfach ignorieren/entfernen alle, dass async Zeug und führen Sie es. Anstatt also eine asynchrone Methode und eine synchrone Version davon zu schreiben, könnten wir nur eine Methode haben, aber der Aufrufer würde entscheiden, ob er das asynchrone Zeug verwenden will oder es einfach ignorieren will, als hätte es nie existiert.
0 Stimmen
@IronHide: Was ist mit Code, der Aufgabenobjekte zurückgibt? no erstellt von
async
?0 Stimmen
@StephenCleary Ich habe keine Ahnung, ich habe nicht an jedes mögliche Szenario gedacht, ich nahm an, dass einige async-Gurus wie Sie selbst es in kürzester Zeit herausfinden werden :) (Nicht sarkastisch gemeint)