web-dev-qa-db-fra.com

Comment exécuter une méthode de tâche asynchrone <T> de manière synchrone?

J'apprenais async/wait et je me suis retrouvé dans une situation où je dois appeler une méthode asynchrone de manière synchrone. Comment puis je faire ça?

Méthode async

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

Utilisation normale:

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

J'ai essayé d'utiliser les éléments suivants: 

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 , mais cela ne fonctionne pas lorsque 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:

System.InvalidOperationException

Message: RunSynchronously ne peut pas être appelé sur une tâche non liée à un délégué.

InnerException: null

Source: mscorlib

Trace de la pile:

          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()
550
Rachel

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

On peut l'appeler en utilisant:

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

Le 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;
        }
    }
}
436
Rachel

Soyez avisé cette réponse a trois ans. Je l'ai écrit en me basant principalement sur une expérience avec .Net 4.0 et très peu avec une version 4.5 surtout avec async-await. En règle générale, c'est une solution simple et agréable, mais elle casse parfois des choses. S'il vous plaît 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 , Task.Result , Task.RunSynchronously


.Net 4.0

Utilisez ceci:

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

x.AsyncWaitHandle.WaitOne();

...ou ca:

task.Start();
task.Wait();
302
AK_

Surpris, personne ne mentionna ceci:

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

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

Pas aussi jolie que certaines des autres méthodes ici, mais elle présente les avantages suivants:

  • il n’avale pas les exceptions (comme Wait)
  • il ne renferme aucune exception lancée dans une AggregateException (comme Result)
  • fonctionne pour Task et Task<T> ( essayez vous-même! )

De plus, étant donné que GetAwaiter est typé, cela devrait fonctionner pour tout objet renvoyé par une méthode asynchrone (telle que ConfiguredAwaitable ou YieldAwaitable), pas seulement les tâches.


edit: Veuillez noter qu'il est possible de bloquer cette approche (ou d'utiliser .Result), à moins que vous ne vous assuriez d'ajouter .ConfigureAwait(false) à chaque fois que vous attendez, pour toutes les méthodes asynchrones pouvant être atteintes à partir de BlahAsync() 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 également le faire.

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
99
James Ko

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 cela ne sera pas une impasse. Les performances sont affectées par le 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; 
70
Michael L Perry

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

Si je lis bien votre question, le code qui appelle l’appel synchrone à une méthode async s’exécute sur un thread de dispatcheur suspendu. Et vous voulez réellement bloquer de manière synchrone jusqu'à ce que la méthode asynchrone soit terminée.

Les méthodes asynchrones en C # 5 sont optimisées en découpant efficacement la méthode en morceaux sous le capot et en renvoyant un Task capable de suivre l'achèvement global du shabang complet. Toutefois, le mode d’exécution des méthodes hachées peut dépendre du type de l’expression transmise à l’opérateur await.

La plupart du temps, vous utiliserez await sur une expression de type Task. L'implémentation du motif await par Task est "intelligente" dans le sens où elle est reportée sur le SynchronizationContext, ce qui provoque essentiellement ce qui suit:

  1. Si le thread qui entre await se trouve sur un thread de boucle de messages Dispatcher ou WinForms, il s'assure que les fragments de la méthode asynchrone se produisent dans le cadre du traitement de la file d'attente des messages.
  2. Si le thread qui entre await se trouve sur un thread de pool de threads, les fragments restants de la méthode async se produisent n'importe où sur le pool de threads.

C'est pourquoi vous rencontrez probablement des problèmes - l'implémentation de la méthode asynchrone tente d'exécuter le reste sur Dispatcher - même si elle est suspendue.

.... sauvegarde! ....

Je dois poser la question pourquoi essayez-vous de bloquer de manière synchrone une méthode asynchrone? Cela irait à l'encontre de la raison pour laquelle la méthode voulait être appelée de manière asynchrone. En général, lorsque vous commencez à utiliser await sur une méthode Dispatcher ou UI, vous souhaitez activer l'ensemble de votre flux d'interface utilisateur de manière asynchrone. Par exemple, si votre pile d'appels ressemblait à quelque chose comme ceci:

  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

Ensuite, une fois que le code a été transformé pour utiliser async, vous obtiendrez généralement

  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

Répondant réellement

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

Une autre solution consiste à exécuter votre méthode asynchrone sur un thread de pool de threads, puis à attendre qu'elle se termine. Cela est facile - vous pouvez le faire avec l'extrait suivant:

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

L'API finale sera Task.Run (...), mais avec le CTP, vous aurez besoin des suffixes Ex ( explication ici ).

22
Theo Yaung

Le moyen le plus simple que j'ai trouvé d'exécuter une tâche de manière synchrone et sans bloquer le thread d'interface utilisateur est d'utiliser RunSynchronously () comme suit:

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

Dans mon cas, un événement se déclenche lorsque quelque chose se produit. Je ne sais pas combien de fois cela se produira. Donc, j'utilise le code ci-dessus dans mon événement, donc chaque fois qu'il se déclenche, il crée une tâche. Les tâches sont exécutées de manière synchrone et cela fonctionne très bien pour moi. J'étais juste surprise de constater que cela m'a pris si longtemps à découvrir comment c'est simple. Habituellement, les recommandations sont beaucoup plus complexes et sujettes aux erreurs. C'était simple et propre.

18
pixel

Je l'ai affronté à quelques reprises, principalement lors de tests unitaires ou dans le développement d'un 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.

16
J. Lennon

J'ai trouvé ce code au composant Microsoft.AspNet.Identity.Core, et cela 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();
}
13
wenhx

Juste un petit mot - cette approche:

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

fonctionne pour WinRT.

Laisse-moi 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 les solutions Windows Store!

Remarque: Cette façon de faire n'est pas thread-safe si vous appelez votre méthode dans une autre méthode async (d'après les commentaires de @Servy)

10
RredCat

Dans votre code, votre première wait pour que la tâche soit exécutée mais vous ne l'avez pas encore démarrée, elle attend donc indéfiniment. Essaye ça:

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

Modifier:

Vous dites que vous obtenez une exception. S'il vous plaît poster plus de détails, y compris trace de la pile.
Mono contient le scénario de test suivant:

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

        Assert.AreEqual (1, val);
}

Vérifiez si cela fonctionne pour vous. Si ce n'est pas le cas, bien que très improbable, vous pourriez avoir une construction étrange de CTP Async. Si cela fonctionne, vous voudrez peut-être examiner ce que le compilateur génère exactement et en quoi l'instanciation Task est différente de cet exemple.

Edit # 2:

J'ai vérifié avec Reflector que l'exception que vous avez décrite se produit lorsque m_action est null. C'est un peu étrange, mais je ne suis pas un expert en CTP Async. Comme je l'ai dit, vous devriez décompiler votre code et voir comment exactement Task est en cours d'instanciation, et pourquoi son m_action est null.


P.S. Quel est le problème avec les downvotes occasionnels? Vous souhaitez élaborer?

9
Dan Abramov

Pourquoi ne pas créer un appel comme:

Service.GetCustomers();

ce n'est pas async.

7
Daniel A. White

utiliser ci-dessous le code snip

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
5
Mahesh

Cette réponse est conçue pour toute personne utilisant WPF for .NET 4.5.

Si vous essayez d'exécuter Task.Run() sur le thread d'interface graphique, 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 d'interface graphique et, le cas échéant, en exécutant la tâche sur le thread de répartition WPF.

Cette classe peut servir de lien entre le monde async/wait et le monde non-async/wait, dans des situations où cela est inévitable, telles que les propriétés MVVM ou les dépendances d'autres API qui n'utilisent pas async/wait.

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

Le simple fait d'appeler .Result; ou .Wait() constitue un risque d'impasse, comme beaucoup l'ont indiqué dans des commentaires. Comme la plupart d’entre nous aiment les oneliners, vous pouvez les utiliser pour .Net 4.5<

Acquérir une valeur via une méthode asynchrone:

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

Appeler de manière synchronisée une méthode asynchrone

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

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

La source:

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

3
Ogglas

Je pense que la méthode d'assistance 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
donttellya

J'ai trouvé que SpinWait fonctionne plutôt bien pour cela.

var task = Task.Run(()=>DoSomethingAsyncronous());

if(!SpinWait.SpinUntil(()=>task.IsComplete, TimeSpan.FromSeconds(30)))
{//Task didn't complete within 30 seconds, fail...
   return false;
}

return true;

L'approche ci-dessus n'a pas besoin d'utiliser .Result ou .Wait (). Il vous permet également de spécifier un délai d'expiration afin que vous ne soyez pas bloqué pour toujours au cas où la tâche ne se termine jamais.

0
Curtis

Testé en .Net 4.6. Cela peut aussi éviter une impasse.

Pour la méthode aysnc, retournez Task.

Task DoSomeWork()

Task.Run(async () => await DoSomeWork()).Wait();

Pour la méthode async, renvoyer Task<T>

Task<T> GetSomeValue()

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

Modifier

Si l'appelant est en cours d'exécution dans le thread du pool de threads (ou si l'appelant est également dans une tâche), il peut toujours provoquer un blocage complet dans certaines situations. 

0
Liang