Comment pourrais-je exécuter une méthode de tâche async de façon synchrone?

j'apprends à propos d'async/Wait, et je me suis retrouvé dans une situation où j'ai besoin d'appeler une méthode async de façon synchrone. Comment puis-je le faire?

méthode Async:

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

usage Normal:

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 aussi essayé une suggestion de ici , mais cela ne fonctionne pas quand le répartiteur est dans un état suspendu.

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 la trace de pile de l'appel RunSynchronously :

Système

.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.CustomControlsControlsMyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:Documents and Settings...MyApplication.CustomControlsControlsMyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:Documents and Settings...MyApplication.CustomControlsControlsMyCustomControl.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.CustomControlsControlsMyCustomControl.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...MyApplicationobjDebugApp.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()
525
demandé sur Hamid Pourjam 0000-00-00 00:00:00

23 réponses

Voici une solution de rechange que j'ai trouvé qui fonctionne pour tous les cas (y compris les répartiteurs suspendus). Ce n'est pas mon code et je travaille encore pour le comprendre, mais ça marche.

il peut être appelé en utilisant:

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

Code est de ici

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
répondu Rachel 2018-03-01 21:47:21

Être informé cette réponse est de trois ans. Je l'ai écrit principalement sur la base d'une expérience avec .Net 4.0, et très peu avec 4.5 en particulier avec async-await . En général, c'est une solution simple, mais elle casse parfois des choses. Veuillez lire la discussion dans les commentaires.

. Net 4.5

il suffit d'utiliser ceci:

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

voir: TaskAwaiter , Tâche.Résultat , de la Tâche.RunSynchronously


. Net 4.0

utilisez ceci:

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

x.AsyncWaitHandle.WaitOne();

...ou ceci:

task.Start();
task.Wait();
290
répondu AK_ 2018-03-01 21:47:48

surpris personne n'a mentionné ceci:

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

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

pas aussi jolie que certaines des autres méthodes ici, mais elle a les avantages suivants:

  • il ne peut pas avaler des exceptions (comme Wait )
  • il ne sera pas envelopper toutes les exceptions lancées dans un AggregateException (comme Result )
  • fonctionne à la fois pour Task et Task<T> ( essayer vous-même! )

aussi, puisque GetAwaiter est tapé du canard, cela devrait fonctionner pour n'importe quel objet qui est retourné d'une méthode async (comme ConfiguredAwaitable ou YieldAwaitable ), pas seulement des tâches.


modifier: s'il Vous Plaît noter qu'il est possible pour cette approche (ou en utilisant .Result ) à l'impasse, sauf si vous vous assurez d'ajouter .ConfigureAwait(false) chaque fois que vous attendez, pour toutes les méthodes async qui peuvent éventuellement être atteint à partir de BlahAsync() (pas seulement ceux qu'il appelle directement). Explication .

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

si vous êtes trop paresseux pour ajouter .ConfigureAwait(false) partout, et que vous ne vous souciez pas de la performance, vous pouvez alternativement faire

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
81
répondu James Ko 2018-02-24 04:32:09

il est beaucoup plus simple d'exécuter la tâche sur le pool de threads, plutôt que d'essayer de tromper le planificateur pour l'exécuter de manière synchrone. De cette façon, vous pouvez être sûr que ce ne sera pas l'impasse. La Performance est affectée en raison du changement de contexte.

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
répondu Michael L Perry 2013-06-13 18:50:54

j'apprends à propos de async / Wait, et je me suis retrouvé dans une situation où j'ai besoin d'appeler une méthode async de façon synchrone. Comment puis-je le faire?

la meilleure réponse est you don't , les détails dépendant de ce qu'est la" situation".

S'agit-il d'une propriété getter/setter? Dans la plupart des cas, il est préférable d'avoir des méthodes asynchrones que "asynchrone propriétés". (Pour plus d'informations, voir mon blog sur asynchrones "propriétés de la 1519170920" ).

est - ce une application MVVM et vous voulez faire la liaison de données asynchrones? Alors utilisez quelque chose comme mon NotifyTask , comme décrit dans mon article MSDN sur la liaison de données asynchrones .

est-ce un constructeur? Alors vous voulez probablement considérer une méthode d'usine asynchrone. (Pour plus d'informations, voir mon billet de blog sur les constructeurs asynchrones ).

il y a presque toujours une meilleure réponse que de faire sync-over-async.

si ce n'est pas possible pour votre situation (et vous le savez en posant une question ici décrivant la situation ), alors je recommande juste d'utiliser le code synchrone. Async tout le chemin est le meilleur; tout synchroniser la voie est la meilleure option. Sync-sur-async n'est pas recommandé.

cependant, il y a une poignée de situations où sync-sur-async est nécessaire. Plus précisément, vous êtes limité par le code d'appel de sorte que vous ayez pour être synchronisé (et n'ayez absolument aucun moyen de repenser ou de restructurer votre code pour permettre l'asynchronie), et vous ayez pour appeler le code asynchrone. Il s'agit d'une très situation rare, mais elle remonte de temps en temps.

Dans ce cas, vous devez utiliser l'un des hacks décrit dans mon article sur brownfield async développement , en particulier:

  • blocage (par exemple, GetAwaiter().GetResult() ). Notez que cela peut causer des blocages (comme je le décris sur mon blog).
  • exécutant le code sur un filetage de type" pool "(par exemple, Task.Run(..).GetAwaiter().GetResult() ). Notez que cela ne fonctionnera que si le code asynchrone peut être exécuté sur un thread pool (i.e., n'est pas dépendant d'une interface utilisateur ou ASP.NET cadre.)
  • boucles de messages imbriquées. Notez que cela ne fonctionnera que si le code asynchrone ne suppose qu'un contexte monofiltre, et non pas un type de contexte spécifique (beaucoup D'UI et de ASP.NET code expect a specific context).

les boucles de messages imbriquées sont les plus dangereuses de toutes les piratages, car elles provoquent re-entrancy . La rentrée est extrêmement délicate à raisonner, et (IMO) est le cause de la plupart des bogues d'application sur Windows. En particulier, si vous êtes sur le thread de L'UI et que vous bloquez une file d'attente de travail (en attendant que le travail asynchrone soit terminé), alors le CLR fait en fait du pompage de message pour vous - il gérera en fait quelques messages Win32 à partir de votre code . Oh, et vous n'avez aucune idée de quels messages-quand Chris Brumme dit " ne serait-il pas génial de savoir exactement ce qui sera pompé? Malheureusement, le pompage est un art noir qui est au-delà de la compréhension mortelle." , alors nous n'avons vraiment aucun espoir de savoir.

ainsi, quand vous bloquez comme ceci sur un fil D'interface utilisateur, vous demandez des ennuis. Une autre citation cbrumme du même article: "de temps en temps, les clients à l'intérieur ou à l'extérieur de l'entreprise découvrent que nous sommes en train de pomper des messages pendant le blocage managé sur un STA [UI thread]. C'est une préoccupation légitime, parce qu'ils savent qu'il est très difficile d'écrire du code robuste en le visage de la réentrance."

Oui, c'est ça. très difficile d'écrire un code robuste face à la réintégration. Et les boucles de message imbriquées forcer vous pour écrire le code qui est robuste face à la rentrée. C'est pourquoi la réponse acceptée (et la plus contestée) à cette question est extrêmement dangereux dans la pratique.

si vous êtes complètement hors de tous autres options - vous ne pouvez pas reconcevoir votre code, vous ne pouvez pas le restructurer pour qu'il soit async - vous êtes forcé par le code d'appel immuable d'être synchronisé - vous ne pouvez pas changer le code en aval pour être synchronisé - vous ne pouvez pas bloquer - vous ne pouvez pas exécuter le code async sur un thread séparé - puis et seulement alors si vous envisagez d'adopter reentrancy.

si vous vous trouvez dans ce coin, je vous recommande d'utiliser quelque chose comme Dispatcher.PushFrame pour les applications WPF , en boucle avec Application.DoEvents pour les applications WinForm, et pour le cas général, mon propre AsyncContext.Run .

38
répondu Stephen Cleary 2017-05-23 12:26:38

Cela fonctionne bien pour moi

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
répondu Clement 2014-07-23 05:42:37

si je lis bien votre question - le code qui veut que l'appel synchrone à une méthode async s'exécute sur un thread de dispatcher suspendu. Et vous voulez réellement synchroniser block ce thread jusqu'à ce que la méthode async soit terminée.

les méthodes Async dans C# 5 sont alimentées en coupant efficacement la méthode en morceaux sous le capot, et en retournant un Task qui peut suivre l'achèvement de l'ensemble du shabang. Cependant, la façon dont les méthodes hachées s'exécutent peut dépendre du type d'expression passé à l'opérateur await .

la plupart du temps, vous utiliserez await sur une expression de type Task . La mise en œuvre par la tâche du modèle await est "intelligente" en ce sens qu'elle renvoie au modèle SynchronizationContext , ce qui entraîne essentiellement la réalisation de ce qui suit:

  1. si le fil entrant dans le await se trouve sur un répartiteur ou WinForms boucle message loop thread, il assure que les morceaux de la méthode async se produit dans le cadre du traitement de la file d'attente de messages.
  2. si le fil entrant dans le await se trouve sur un filetage en pool, alors les morceaux restants de la méthode async se trouvent n'importe où sur le filetage en pool.

C'est pourquoi vous rencontrez probablement des problèmes - l'implémentation de la méthode async essaye d'exécuter le reste sur le répartiteur - même si c'est suspendre.

.... la sauvegarde! ....

je me pose la question, pourquoi essayez-vous de façon synchrone bloc sur une méthode asynchrone? Faire cela irait à l'encontre du but sur la raison pour laquelle la méthode a voulu être appelé asynchrone. En général, quand vous commencez à utiliser await sur un répartiteur ou la méthode D'UI, vous voudrez tourner votre flux D'UI entier async. Par exemple, si votre pile est quelque chose comme suit:

  1. [haut] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing() - WPF ou WinForms Code
  6. [boucle de Message] - WPF ou WinForms boucle de Message

puis une fois le le code a été transformé pour utiliser async, vous finirez généralement avec

  1. [haut] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing() - WPF ou WinForms Code
  6. [boucle de Message] - WPF ou WinForms boucle de Message

En Fait, Répondre

la classe AsyncHelpers ci-dessus fonctionne réellement parce qu'elle se comporte comme une boucle de message imbriquée, mais elle installe son propre mécanicien parallèle au répartiteur plutôt que d'essayer d'exécuter sur le répartiteur lui-même. C'est une solution pour votre problème.

une autre solution consiste à exécuter votre async méthode sur un fil threadpool, puis attendre qu'il complète. Cela est facile - vous pouvez le faire avec l'extrait suivant:

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

la dernière API sera la tâche.Exécuter.(..), mais avec le CTP vous aurez besoin des suffixes Ex ( explication ici ).

18
répondu Theo Yaung 2018-08-28 23:27:00

le moyen le plus simple que j'ai trouvé pour exécuter la tâche de façon synchrone et sans bloquer le thread UI est D'utiliser RunSynchronously() comme:

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

dans mon cas, j'ai un événement qui déclenche quand quelque chose se produit. Je ne sais pas combien de fois il va se produire. Donc, j'utilise le code ci-dessus dans mon cas, donc chaque fois qu'il démarre, il crée une tâche. Les tâches sont exécutées de façon synchrone et cela fonctionne très bien pour moi. J'étais juste surpris que ça m'ait pris autant de temps pour le découvrir en considérant comment il est simple. Habituellement, les recommandations sont beaucoup plus complexes et sujettes aux erreurs. C'était il est simple et propre.

16
répondu pixel 2016-10-25 23:22:45

Je l'ai affronté à quelques reprises, principalement dans le cadre de tests unitaires ou d'un développement de service windows. Actuellement, j'utilise toujours cette fonctionnalité:

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

c'est simple, facile et je n'ai eu aucun problème.

15
répondu J. Lennon 2014-03-06 13:32:38

j'ai trouvé ce code chez Microsoft.AspNet.Identité.Composant de base, et ça fonctionne.

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
répondu wenhx 2014-12-02 02:23:57

juste une petite note - cette approche:

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

travaux pour WinRT.

laissez-moi vous expliquer:

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

de plus cette approche ne fonctionne que pour Windows Store solutions!

Note: Cette voie n'est pas thread-safe si vous appelez votre méthode à l'intérieur d'autres méthode async (selon les commentaires de @Servy)

9
répondu RredCat 2015-02-19 15:53:06

dans votre code, votre premier attend pour la tâche à exécuter, mais vous ne l'avez pas commencé donc il attend indéfiniment. Essayez ceci:

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

Edit:

vous dites que vous obtenez une exception. Veuillez poster plus de détails, y compris la trace de la pile.

Le Mono contient le cas d'essai suivant:

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

        Assert.AreEqual (1, val);
}

vérifier si ce qui fonctionne pour vous. Si ce n'est pas le cas, bien que très improbable, vous pourriez avoir une construction étrange D'async CTP. Si cela fonctionne, vous pourriez vouloir examiner ce que le compilateur génère exactement et comment l'instanciation Task est différente de cet échantillon.

Edit #2:

j'ai vérifié avec réflecteur que l'exception que vous avez décrite se produit lorsque m_action est null . C'est un peu bizarre, mais je ne suis pas un expert en PCC asynchrone. Comme je l'ai dit, vous devriez décompiler votre code et voir comment exactement Task est instancié n'importe comment comment son m_action est null .


P. S .. Que se passe-t-il avec les descentes occasionnelles? Soins à élaborer?

8
répondu Dan Abramov 2013-12-17 13:08:48

pourquoi ne pas créer un appel comme:

Service.GetCustomers();

ce n'est pas async.

7
répondu Daniel A. White 2011-02-23 18:20:34

utiliser le code ci-dessous snip

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
5
répondu Mahesh 2016-10-19 04:13:30

cette réponse est conçue pour quiconque utilise WPF pour .NET 4.5.

si vous tentez d'exécuter Task.Run() sur le thread GUI, alors task.Wait() sera suspendu indéfiniment, si vous n'avez pas le mot-clé async dans votre définition de fonction.

cette méthode d'extension résout le problème en vérifiant si nous sommes sur le thread GUI, et si oui, en exécutant la tâche sur le thread WPF dispatcher.

cette classe peut agir comme la colle entre le monde async/attente et le monde non-async/attente, dans les situations où il est inévitable, telles que les propriétés MVVM ou les dépendances sur D'autres API qui n'utilisent pas async/attente.

/// <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
répondu Contango 2014-09-13 12:51:41

simplement appeler .Result; ou .Wait() est un risque d'impasse comme beaucoup l'ont dit dans les commentaires. Comme la plupart d'entre nous aimons les oneliners, vous pouvez les utiliser pour .Net 4.5<

acquisition d'une valeur par une méthode asynchrone:

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

Syncronously de l'appel d'une méthode async

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

aucun problème d'impasse ne se produira en raison de l'utilisation de Task.Run .

Source:

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

3
répondu Ogglas 2017-09-25 13:49:42

je pense que la méthode d'aide suivante pourrait également résoudre le problème.

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

peut être utilisé de la manière suivante:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
1
répondu donttellya 2016-02-16 09:58:37

vous pouvez utiliser CoRoutines . Voir Caliburn.Mise en œuvre du Micro . J'ai une implémentation personnalisée ici .

0
répondu Jone Polvora 2013-02-01 18:20:49

Ce qui fonctionne pour moi

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
répondu Dan Nguyen 2018-07-27 15:20:06

sur wp8:

enveloppe:

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

l'Appeler:

GetCustomersSynchronously();
-3
répondu user2113284 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
répondu ksemenenko 2013-09-27 20:29:16

ou vous pouvez simplement aller avec:

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

pour cette compilation, assurez-vous de faire référence à l'extension assembly:

System.Net.Http.Formatting
-4
répondu user2057962 2016-03-08 23:28:27

essayez le code suivant il fonctionne pour moi:

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
répondu gandhraj gayakwad 2016-10-13 16:18:53