Je suis en train de me familiariser avec async/await, et je me suis retrouvé dans une situation où je dois appeler une méthode async de manière synchrone. Comment puis-je le faire ?
Méthode asynchrone :
public async Task<Customers> GetCustomers()
{
return await Service.GetCustomersAsync();
}
Utilisation normale :
public async void GetCustomers()
{
customerList = await GetCustomers();
}
J'ai essayé d'utiliser ce qui suit :
Task<Customer> task = GetCustomers();
task.Wait()
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)
J'ai également essayé une suggestion de ici Cependant, cela ne fonctionne pas lorsque le répartiteur est en état de suspension.
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();
}
Voici l'exception et le suivi de pile de l'appel RunSynchronously
:
System.InvalidOperationException
Message : RunSynchronously ne peut pas être appelé sur une tâche non liée à un délégué.
InnerException : null
Source : : 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()
51 votes
La meilleure réponse à la question "Comment puis-je appeler une méthode asynchrone de manière synchrone" est "ne le faites pas". Il existe hacks pour essayer de le forcer à fonctionner, mais ils ont tous des pièges très subtils. Au lieu de cela, faites marche arrière et corrigez le code qui vous rend "nécessaire" de faire cela.
83 votes
Stephen Cleary est tout à fait d'accord, mais parfois c'est tout simplement inévitable, par exemple lorsque votre code dépend d'une API tierce qui n'utilise pas async/await. En outre, si vous liez des propriétés WPF en utilisant MVVM, il est littéralement impossible d'utiliser async/await car cela n'est pas pris en charge par les propriétés.
5 votes
@StephenCleary Pas toujours. Je suis en train de construire une DLL qui sera importée en GeneXus . Il ne supporte pas les mots-clés async/await, je dois donc utiliser uniquement des méthodes synchrones.
0 votes
@DineiA.Rockenbach : Trois meilleures alternatives me viennent immédiatement à l'esprit : 1) Demander à GeneXus d'ajouter le support des méthodes asynchrones ; 2) Implémenter un callback/événement au lieu d'utiliser
Task
(souvent nécessaire pour l'interopérabilité inter-langues) ; 3) utiliser du code synchrone à 100%, de sorte que la synchronisation sur l'asynchronisation n'est même pas nécessaire. Chacune de ces solutions est une meilleure alternative à l'utilisation de bidouillages douteux.7 votes
@StephenCleary 1) GeneXus est un outil de 3ème partie et je n'ai pas accès à son code source ; 2) GeneXus n'a même pas d'implémentations de "fonctions", donc je ne peux pas réaliser comment je pourrais implémenter un "callback" avec ce type de chose. Il s'agirait certainement d'une solution plus difficile que l'utilisation de
Task
de manière synchrone ; 3) j'intègre GeneXus avec Pilote MongoDB C# qui exposent certaines méthodes uniquement de manière asynchrone.6 votes
StephenCleary C'est une belle théorie, mais "ne le faites pas" a ce problème inhérent qu'il "ne fonctionne pas". C# m'interdit activement d'utiliser
await
à l'intérieur de blocs synchronisés. Dois-je demander à Microsoft de changer de langage ? Ou dois-je laisser tomber la synchronisation et accepter des structures de données désordonnées ?async
est ce cancer, pas tellement la GPL. Une fois que vous l'avez, vous ne pouvez pas vous en débarrasser.2 votes
@ygoe : Utilisez un verrou compatible avec l'asynchronisme, tel que
SemaphoreSlim
.2 votes
L'absence de StackOverflow les plus grands noms même fin 2016 suggère fortement qu'il n'y a toujours pas de méthode simple pour le faire. BTW. Je me demande pourquoi il n'y a pas
runsync
opérateur.0 votes
Duplicata possible de Comment appeler une méthode asynchrone à partir d'une méthode synchrone en C# ?
0 votes
Lier un Questions et réponses sur le sujet sur la façon de faire cela sur un thread UI.
0 votes
@StephenCleary pourquoi .net ne fournit pas quelque chose comme la méthode ".RunAsSynchronous()" que nous pouvons appeler sur le type de tâche ? ce genre de méthode ignorerait/supprimerait simplement tous ces trucs asynchrones et l'exécuterait. Ainsi, au lieu d'écrire des méthodes asynchrones et des versions synchrones de celles-ci, nous pourrions avoir une seule méthode, mais l'appelant déciderait s'il veut utiliser les fonctions asynchrones ou les ignorer complètement comme si elles n'avaient jamais existé.
0 votes
@IronHide : Qu'en est-il du code qui renvoie des objets de tâche pas créé par
async
?0 votes
@StephenCleary Je n'en ai aucune idée, je n'ai pas pensé à tous les scénarios possibles, j'ai supposé que des gourous de l'asynchronisme comme vous le feriez en un rien de temps :) (Je ne suis pas sarcastique)
0 votes
On m'a dit de ne pas utiliser
var x = SomeAsyncMethod().Result
pour exécuter une tâche asynchrone à l'intérieur d'une méthode non asynchrone, car cela bloquerait le thread principal et provoquerait un blocage. Est-ce toujours valable en 2022 pour les applications .NET 6 ? Et si j'ai cette ligne dans un projet .NET Framework 4.7 ? J'ai vu des personnes suggérervar x = Task.Run( async ()=> await SomeAsyncMethod()).Result
pour éviter les blocages. Est-ce que ça va marcher ?