关于c#:我如何运行异步任务< T>

How would I run an async Task<T> method synchronously?

我正在学习异步/等待,遇到了需要同步调用异步方法的情况。我该怎么做?

异步方法:

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

正常使用:

1
2
3
4
public async void GetCustomers()
{
    customerList = await GetCustomers();
}

我尝试使用以下方法:

1
2
3
4
5
6
7
8
Task<Customer> task = GetCustomers();
task.Wait()

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

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

我也尝试过这里的一个建议,但是当调度员处于挂起状态时,它不起作用。

1
2
3
4
5
6
7
8
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

Message: RunSynchronously may not be called on a task unbound to a delegate.

InnerException: null

Source: mscorlib

StackTrace:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
          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()


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

可通过以下方式调用:

customerList = AsyncHelpers.RunSync>(() => GetCustomers());

代码来自这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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;
        }
    }
}


请注意,这个答案是三岁。我写这篇文章的主要依据是.NET 4.0的经验,而4.5的经验很少,尤其是async-await的经验。一般来说,这是一个很好的简单的解决方案,但有时会使事情破裂。请阅读评论中的讨论。

.NET 4.5

只要用这个:

1
2
3
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();

见:TaskAuthor,任务结果任务.同步运行

.NET 4

使用此:

1
2
3
4
var x = (IAsyncResult)task;
task.Start();

x.AsyncWaitHandle.WaitOne();

…或者这个:

1
2
task.Start();
task.Wait();


令人惊讶的是,没有人提到:

1
2
3
4
5
6
public Task<int> BlahAsync()
{
    // ...
}

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

虽然不如其他方法漂亮,但它有以下好处:

  • 它不接受例外(如Wait)
  • 它不会包装在AggregateException中抛出的任何异常(如Result)。
  • TaskTask的工程(IL/K4ZWLGDG5GBAYGTXAFWKFSDCAOLAIOYBKKBYBOQAHGCB5K0GDWIO2DSAE2YAMYYYYYBFZZZZZZHHWZ93DW1QZZZGBKCBCBHW8/LuYADCZMDZMDZZMZMZMDZZMZMDOYDZMDZMZMDZZZMZMDZMDZZMDZMDZZMDZZZZZZZZZZZZC37RYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZC37RYZZZZZZZZZZZZZZZja0smce1gqajqyd5kzwlwmciyac87grgaxjibtqtu3shh0fhembfpb9vtyaa="rel="Noreferrer">自己试试看!)

另外,由于GetAwaiter是duck类型的,因此对于从异步方法(如ConfiguredAwaitableYieldAwaitable返回的任何对象,这都应该有效,而不仅仅是任务。

编辑:请注意,这种方法(或使用.Result)可能会死锁,除非您确保在每次等待时都添加.ConfigureAwait(false),用于从BlahAsync()可以访问的所有异步方法(而不仅仅是它直接调用的方法)。说明。

1
2
3
4
5
6
7
// 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),而且你不关心性能,你可以选择这样做。

16


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

1
2
3
4
5
6
7
8
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;


I'm learning about async/await, and ran into a situation where I need to call an async method synchronously. How can I do that?

最好的答案是你没有,细节取决于"情况"是什么。

它是属性getter/setter吗?在大多数情况下,使用异步方法比使用"异步属性"要好。(有关详细信息,请参阅我关于异步属性的博客文章)。

这是一个MVVM应用程序,您想进行异步数据绑定吗?然后使用类似于我的NotifyTask的东西,如我关于异步数据绑定的msdn文章中所描述的。

它是一个建设者吗?然后您可能想考虑一个异步工厂方法。(有关更多信息,请参阅我关于异步构造函数的博客文章)。

几乎总有比异步同步更好的答案。

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

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

在这种情况下,您需要使用我关于Brownfieldasync开发的文章中描述的其中一个黑客,具体来说:

  • 阻塞(如GetAwaiter().GetResult())。注意,这会导致死锁(正如我在博客中所描述的)。
  • 在线程池线程上运行代码(例如,Task.Run(..).GetAwaiter().GetResult())。请注意,只有在异步代码可以在线程池线程上运行(即,不依赖于UI或ASP.NET上下文)时,这才有效。
  • 嵌套的消息循环。请注意,只有当异步代码只假设一个单线程上下文,而不是特定的上下文类型时(许多UI和ASP.NET代码都需要特定的上下文),这才会起作用。

嵌套的消息循环是所有黑客中最危险的,因为它会导致重新进入。重新进入是非常棘手的原因,和(IMO)是大多数应用程序错误的原因在Windows上。特别是,如果您在UI线程上并且阻塞了一个工作队列(等待异步工作完成),那么clr实际上为您进行了一些消息抽取——它实际上会处理代码中的一些win32消息。哦,你也不知道是什么信息——当克里斯·布鲁姆说:"确切地知道什么会被激励是不是很棒?"不幸的是,抽气是一种超越凡人理解的黑色艺术。

所以,当你在一个用户界面线程上这样阻塞时,你就是在自找麻烦。另一个来自同一篇文章的cRumme引用:"公司内部或外部的客户不时发现,我们在sta[ui线程上的托管阻塞期间正在发送消息。"这是一个合理的顾虑,因为他们知道很难编写面对可重入性的健壮代码。"

是的,是的。很难编写面对可重入性的健壮代码。嵌套的消息循环迫使您编写面对可重入性的健壮代码。这就是为什么这个问题的公认(也是最乐观的)答案在实践中极其危险的原因。

如果你完全没有其他的选择-你不能重新设计你的代码,你不能把它重组成异步的-你被不可改变的调用代码强迫去同步-你不能改变下游代码的同步-你不能阻止-你不能在一个单独的线程上运行异步代码-然后,只有在那时你才应该考虑接受重新输入兰西。

如果你真的发现自己在这个角落里,我建议你在wpf应用程序中使用Dispatcher.PushFrame,在winform应用程序中使用Application.DoEvents,在一般情况下,使用我自己的AsyncContext.Run


这对我很有效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
    }
}


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

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

大多数时候,您将在Task类型的表达式上使用await。任务执行await模式是"智能"的,因为它遵从SynchronizationContext模式,这基本上导致了以下情况的发生:

  • 如果进入await的线程位于调度程序或winforms消息循环线程上,它将确保异步方法的块作为消息队列处理的一部分出现。
  • 如果进入await的线程在线程池线程上,那么异步方法的剩余块会出现在线程池的任何地方。
  • 这就是您可能遇到问题的原因——异步方法实现试图在调度程序上运行其余的方法——即使它被挂起了。

    …备份!…

    我不得不问一个问题,为什么您要尝试在异步方法上同步阻塞?这样做会破坏异步调用方法的目的。通常,当您开始在调度器或UI方法上使用await时,您将希望使整个UI流异步化。例如,如果您的调用堆栈如下所示:

  • [上]WebRequest.GetResponse()
  • YourCode.HelperMethod()
  • YourCode.AnotherMethod()
  • YourCode.EventHandlerMethod()
  • [UI Code].Plumbing()WPFWinForms
  • [消息循环]-WPFWinForms消息循环
  • 然后,一旦代码被转换为使用异步,您通常会

  • [上]WebRequest.GetResponseAsync()
  • YourCode.HelperMethodAsync()
  • YourCode.AnotherMethodAsync()
  • YourCode.EventHandlerMethodAsync()
  • [UI Code].Plumbing()WPFWinForms
  • [消息循环]-WPFWinForms消息循环
  • 实际回答

    上面的AsyncHelpers类实际上可以工作,因为它的行为类似于嵌套的消息循环,但是它向调度程序安装自己的并行机制,而不是尝试在调度程序本身上执行。这是解决你问题的一个方法。

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

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

    最后一个API将是task.run(…),但是使用ctp,您将需要ex后缀(此处解释)。


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

    1
    2
    3
    4
    5
    Task t = new Task(() =>
    {
       //.... YOUR CODE ....
    });
    t.RunSynchronously();

    在我的例子中,我有一个事件发生时会触发。我不知道会发生多少次。所以,我在事件中使用上面的代码,所以每当它触发时,它就会创建一个任务。任务是同步执行的,它对我很有用。考虑到这件事有多简单,我花了这么长时间才发现这一点,这让我很惊讶。通常,建议更复杂,更容易出错。这就是它的简单和干净。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
            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");

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


    我在microsoft.aspnet.identity.core组件中找到了这段代码,它运行正常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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();
    }


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

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

    适用于Winrt。

    让我解释一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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 Store解决方案!

    注意:如果您在其他异步方法内部调用您的方法(根据@servy的注释),那么这种方法是不安全的。


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

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

    编辑:

    你说你有例外。请发布更多详细信息,包括堆栈跟踪。Mono包含以下测试用例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [Test]
    public void ExecuteSynchronouslyTest ()
    {
            var val = 0;
            Task t = new Task (() => { Thread.Sleep (100); val = 1; });
            t.RunSynchronously ();

            Assert.AreEqual (1, val);
    }

    检查一下这个是否适合你。如果不是,尽管可能性很小,但您可能有一些奇怪的异步CTP构建。如果它确实有效,您可能需要检查编译器究竟生成了什么,以及Task实例化与此示例有何不同。

    编辑第2页:

    我用Reflector检查了一下,当m_actionnull时,会发生您所描述的异常。这有点奇怪,但我不擅长异步CTP。如我所说,您应该对代码进行反编译,看看Task是如何被实例化的,以及它的m_action是如何被实例化的。

    P.S.偶尔出现的落选是怎么回事?想详细说明吗?


    为什么不创建这样的呼叫:

    1
    Service.GetCustomers();

    这不是异步的。


    使用下面的代码截图

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


    正如许多人在评论中所说的那样,简单地称为.Result;.Wait()是出现僵局的风险。因为我们大多数人都喜欢OneLines,所以你可以把它用于.Net 4.5<

    通过异步方法获取值:

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

    同步调用异步方法

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

    使用Task.Run不会出现死锁问题。

    来源:

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


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

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

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

    在不可避免的情况下,此类可以充当异步/等待世界和非异步/等待世界之间的粘合剂,例如MVVM属性或对不使用异步/等待的其他API的依赖性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    /// <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();
            }
        }
    }

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

    对于aysnc方法返回Task

    1
    2
    3
    Task DoSomeWork()

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

    对于异步方法返回Task

    1
    2
    3
    Task<T> GetSomeValue()

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

    编辑:

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


    我认为下面的辅助方法也可以解决这个问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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;
        }

    可按以下方式使用:

    1
    InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);


    我发现spinwait对于这个很有效。

    1
    2
    3
    4
    5
    6
    7
    8
    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()。它还允许您指定一个超时,这样在任务永远不会完成的情况下,您就不会永远陷入困境。


    这对我有用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    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();
            }
        }
    }

    关于WP8:

    包装它:

    1
    2
    3
    4
    5
    6
    7
    8
    Task GetCustomersSynchronously()
    {
        Task t = new Task(async () =>
        {
            myCustomers = await GetCustomers();
        }
        t.RunSynchronously();
    }

    称之为:

    1
    GetCustomersSynchronously();


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
        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;
            }
        }

    或者你可以选择:

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

    要进行编译,请确保引用扩展程序集:

    1
    System.Net.Http.Formatting

    尝试以下代码它对我有效:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    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);
        }
    }