我如何同步运行异步Task <t>方法?</t>

时间:2011-02-23 18:18:28

标签: c# asynchronous c#-5.0 async-await

我正在学习async / await,并遇到了需要同步调用异步方法的情况。我怎么能这样做?

异步方法:

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

正常使用:

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

我尝试过使用以下内容:

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

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

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

我还尝试了here的建议,但是当调度程序处于暂停状态时它不起作用。

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

以下是调用RunSynchronously的异常和堆栈跟踪:

  

System.InvalidOperationException

     

消息:对于未绑定到委托的任务,可能无法调用RunSynchronously。

     

InnerException :null

     

来源:mscorlib

     

栈跟踪

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

25 个答案:

答案 0 :(得分:422)

这是我发现适用于所有情况(包括暂停的调度程序)的解决方法。这不是我的代码,我仍然在努力完全理解它,但它确实有效。

可以使用以下方式调用:

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

代码来自here

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

答案 1 :(得分:308)

被告知这个答案是三岁。我的写作主要是基于.Net 4.0的经验,而4.5很少,特别是async-await。 一般来说,这是一个很好的简单解决方案,但它有时会破坏事物。请阅读评论中的讨论。

.Net 4.5

请使用:

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

请参阅: TaskAwaiterTask.ResultTask.RunSynchronously


.Net 4.0

使用此:

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

x.AsyncWaitHandle.WaitOne();

......或者这个:

task.Start();
task.Wait();

答案 2 :(得分:103)

惊讶没有人提到这个:

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

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

不像其他一些方法那样漂亮,但它有以下好处:

  • 它不会吞下异常(例如Wait
  • 它不会包装AggregateException中抛出的任何例外(例如Result
  • 适用于TaskTask<T>try it out yourself!

此外,由于GetAwaiter是鸭式的,因此这适用于从异步方法(如ConfiguredAwaitableYieldAwaitable)返回的任何对象,而不仅仅是任务。

修改:请注意,此方法(或使用.Result)可能会死锁,除非您确保每次都添加.ConfigureAwait(false)等待,可以从BlahAsync()(而不仅仅是它直接调用的那些)可以到达的所有异步方法。 Explanation

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

如果您懒得在任何地方添加.ConfigureAwait(false),并且您不关心性能,您可以选择

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()

答案 3 :(得分:73)

在线程池上运行任务要简单得多,而不是试图欺骗调度程序同步运行它。这样你就可以确定它不会死锁。由于上下文切换,性能受到影响。

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; 

答案 4 :(得分:48)

  

我正在学习async / await,并遇到了需要同步调用异步方法的情况。我怎么能这样做?

最好的答案是你不,细节取决于&#34;情况&#34;是

它是属性getter / setter吗?在大多数情况下,拥有异步方法比使用&#34;异步属性&#34;更好。 (有关详细信息,请参阅my blog post on asynchronous properties)。

这是一个MVVM应用程序,您想要进行异步数据绑定吗?然后使用NotifyTask之类的内容,如MSDN article on asynchronous data binding中所述。

它是构造函数吗?那么您可能想要考虑异步工厂方法。 (有关详细信息,请参阅我的blog post on asynchronous constructors)。

除了执行同步异步之外,几乎总是更好的答案。

如果您的情况不可能(并且您通过在此处提出问题来了解此情况描述情况),那么我建议您只使用同步代码。一路异步是最好的;一路同步是第二好的。建议不要使用异步同步。

但是,有一些情况需要同步异步同步。具体来说,您受到调用代码的约束,因此您具有同步(并且绝对无法重新思考或重新构造代码以允许异步),来调用异步代码。这是一种非常罕见的情况,但确实会不时出现。

在这种情况下,您需要使用我在brownfield async development上的文章中描述的其中一个黑客,具体来说:

  • 阻止(例如GetAwaiter().GetResult())。请注意this can cause deadlocks(正如我在博客中所述)。
  • 在线程池线程上运行代码(例如,Task.Run(..).GetAwaiter().GetResult())。请注意,这只有在异步代码可以在线程池线程上运行时才会起作用(即,不依赖于UI或ASP.NET上下文)。
  • 嵌套的消息循环。请注意,这仅在异步代码仅假定单线程上下文而非特定上下文类型(许多UI和ASP.NET代码期望特定上下文)时才有效。

嵌套的消息循环是所有黑客中最危险的,因为它会导致re-entrancy。 Re-entrancy非常难以理解,而且(IMO)是Windows上大多数应用程序错误的原因。特别是,如果您在UI线程上并且在工作队列上阻塞(等待异步工作完成),那么CLR实际上为您做了一些消息 - 它实际上会处理一些Win32来自您代码中的消息 。哦,你不知道哪些消息 - 当 Chris Brumme says "Wouldn’t it be great to know exactly what will get pumped? Unfortunately, pumping is a black art which is beyond mortal comprehension."时,我们真的没有希望知道。

所以,当你在UI线程上这样阻止时,你就会遇到麻烦。来自同一篇文章的另一篇文章引用:&#34;公司内部或外部的客户不时发现我们在STA [UI线程]上的托管阻止期间抽取消息。这是一个合理的问题,因为他们知道编写在重入时面前强大的代码非常困难。&#34;

是的,确实如此。 非常难以编写面对可重入性的强大代码。并且嵌套的消息循环强制你可以编写在重入时面对的强大代码。这就是the accepted (and most-upvoted) answer for this question在实践中非常危险的原因。

如果您完全没有其他所有选项 - 您无法重新设计代码,则无法将其重组为异步 - 您可以通过不可更改的调用代码强制同步 - 您可以&#39 ; t将下游代码更改为同步 - 您无法阻止 - 您无法在单独的线程上运行异步代码 - 然后然后只有才能考虑拥抱重入。

如果您确实发现自己就在这个角落,我建议您使用类似Dispatcher.PushFrame for WPF apps的内容,使用Application.DoEvents循环使用WinForm应用,对于一般情况,我自己AsyncContext.Run。< / p>

答案 5 :(得分:23)

这对我很有用

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

答案 6 :(得分:22)

如果我正确地阅读您的问题 - 希望同步调用异步方法的代码正在暂停的调度程序线程上执行。并且您希望实际上同步阻止该线程,直到异步方法完成。

C#5中的异步方法是通过有效地将方法切割成引擎盖下的方法来提供动力,并返回一个Task来跟踪整个shabang的整体完成情况。但是,切碎方法的执行方式取决于传递给await运算符的表达式的类型。

大多数情况下,您将在await类型的表达式上使用Task。任务对await模式的实现是“聪明的”,因为它遵循SynchronizationContext,这基本上会导致以下情况发生:

  1. 如果进入await的线程位于Dispatcher或WinForms消息循环线程上,它将确保异步方法的块在处理消息队列时发生。
  2. 如果进入await的线程在线程池线程上,那么异步方法的剩余块将出现在线程池的任何位置。
  3. 这就是为什么你可能遇到问题 - 异步方法实现试图在Dispatcher上运行其余部分 - 即使它已被暂停。

    ....备份! ....

    我必须问一个问题,为什么你试图同步阻止异步方法?这样做会破坏为什么要异步调用该方法的目的。通常,当您在Dispatcher或UI方法上开始使用await时,您将希望将整个UI流转为异步。例如,如果您的callstack类似于以下内容:

    1. [回到顶部] WebRequest.GetResponse()
    2. YourCode.HelperMethod()
    3. YourCode.AnotherMethod()
    4. YourCode.EventHandlerMethod()
    5. [UI Code].Plumbing() - WPFWinForms代码
    6. [消息循环] - WPFWinForms消息循环
    7. 然后,一旦代码转换为使用异步,您通常会以

      结束
      1. [回到顶部] WebRequest.GetResponseAsync()
      2. YourCode.HelperMethodAsync()
      3. YourCode.AnotherMethodAsync()
      4. YourCode.EventHandlerMethodAsync()
      5. [UI Code].Plumbing() - WPFWinForms代码
      6. [消息循环] - WPFWinForms消息循环

      7. 实际回答

        上面的AsyncHelpers类实际上是有效的,因为它的行为类似于嵌套的消息循环,但是它将自己的并行机制安装到Dispatcher而不是尝试在Dispatcher本身上执行。这是解决问题的一种方法。

        另一种解决方法是在线程池线程上执行异步方法,然后等待它完成。这样做很容易 - 您可以使用以下代码段来完成此操作:

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

        最终的API将是Task.Run(...),但是使用CTP,您将需要Ex后缀(explanation here)。

答案 7 :(得分:18)

我发现同步运行任务而不阻塞UI线程的最简单方法是使用RunSynchronously(),如:

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

在我的情况下,我发生了一个事件,当事情发生时会触发。我不知道会发生多少次。所以,我在我的事件中使用上面的代码,因此每当它触发时,它都会创建一个任务。任务同步执行,对我来说非常有用。我很惊讶我花了这么长时间才发现这是多么简单。通常,建议要复杂得多,容易出错。这是简单而干净的。

答案 8 :(得分:16)

我已经面对过几次,主要是在单元测试或Windows服务开发中。目前我总是使用这个功能:

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

这很简单,容易,我也没有问题。

答案 9 :(得分:13)

我在Microsoft.AspNet.Identity.Core组件中找到了这个代码,它可以工作。

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

答案 10 :(得分:11)

只是一点注意 - 这种方法:

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

适用于WinRT。

让我解释一下:

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

此外,此方法仅适用于Windows应用商店解决方案!

注意:如果你在其他异步方法中调用你的方法,这种方式不是线程安全的(根据@Servy的评论)

答案 11 :(得分:9)

在您的代码中,您的第一个等待以执行任务,但您尚未启动它,因此它会无限期地等待。试试这个:

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

修改

你说你得到了一个例外。请发布更多详细信息,包括堆栈跟踪 Mono contains以下测试用例:

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

        Assert.AreEqual (1, val);
}

检查这是否适合您。如果不是,尽管不太可能,你可能会有一些奇怪的Async CTP版本。如果它确实有效,您可能想要检查编译器生成的确切内容以及Task实例化与此示例的不同之处。

编辑#2:

我使用Reflector检查了当m_actionnull时您所描述的异常。这有点奇怪,但我不是Async CTP的专家。正如我所说,您应该反编译代码,看看Task m_action的{​​{1}}是如何实例化的null


P.S。什么是偶尔的downvotes处理?注意详细说明?

答案 12 :(得分:6)

为什么不创建像:

之类的电话
Service.GetCustomers();

这不是异步。

答案 13 :(得分:5)

使用下面的代码片段

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

答案 14 :(得分:3)

此答案适用于使用WPF for .NET 4.5的任何人。

如果您尝试在GUI线程上执行Task.Run(),那么如果您的函数定义中没有task.Wait()关键字,async将无限期挂起。

此扩展方法通过检查我们是否在GUI线程上来解决问题,如果是,则在WPF调度程序线程上运行任务。

在不可避免的情况下,这个类可以充当async / await世界和非async / await世界之间的粘合剂,例如MVVM属性或不使用async / await的其他API的依赖。 / p>

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

答案 15 :(得分:3)

正如许多人在评论中所说的那样,简单地调用version: "3" services: docker-build: build: ./ ports: - "4242:5000" environment: - "PYTHONUNBUFFERED=1" .Result;是一种陷入僵局的风险。由于我们大多数人都喜欢oneliners,因此您可以将这些内容用于.Wait()

通过异步方法获取值:

.Net 4.5<

同步调用异步方法

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

由于使用了Task.Run(() => asyncMethod()).Wait();

,不会出现死锁问题

来源:

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

答案 16 :(得分:2)

在.Net 4.6中测试。它还可以避免死锁。

对于aysnc方法,返回Task

Task DoSomeWork()

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

对于异步方法,返回Task<T>

Task<T> GetSomeValue()

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

修改

如果调用方正在线程池线程中运行(或者调用方也处于任务中),则在某些情况下仍可能导致死锁。

答案 17 :(得分:1)

我认为以下帮助方法也可以解决问题。

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

可以使用以下方式:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);

答案 18 :(得分:0)

我发现SpinWait对此非常有效。

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;

上面的方法不需要使用.Result或.Wait()。它还可以让您指定超时时间,以免万一任务永不完成,您就不会永远被困住。

答案 19 :(得分:0)

注意:我认为,不建议您按照最佳做法更改操作的性质(如果它是异步的),那么最好的事情就是按原样进行处理(一直保持异步)。这样,您可以获得其他好处,例如并行处理/多线程等。

看到其他答案没有使用这种方法,我也想在这里发布:

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

答案 20 :(得分:-1)

这对我有用

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

答案 21 :(得分:-3)

关于wp8:

包裹它:

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

称之为:

GetCustomersSynchronously();

答案 22 :(得分:-4)

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

答案 23 :(得分:-5)

或者你可以选择:

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

为了编译,请确保引用扩展程序集:

System.Net.Http.Formatting

答案 24 :(得分:-9)

尝试以下适合我的代码:

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