如何同步运行异步Task <T>方法?

Rac*_*hel 606 c# asynchronous async-await c#-5.0

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

异步方法:

public async Task<Customers> GetCustomers()
{
    return await Service.GetCustomersAsync();
}
Run Code Online (Sandbox Code Playgroud)

正常使用:

public async void GetCustomers()
{
    customerList = await GetCustomers();
}
Run Code Online (Sandbox Code Playgroud)

我尝试过使用以下内容:

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

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

Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)
Run Code Online (Sandbox Code Playgroud)

我也从这里尝试了一个建议,但是当调度程序处于暂停状态时它不起作用.

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();
}
Run Code Online (Sandbox Code Playgroud)

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

System.InvalidOperationException

消息:可能无法在未绑定到委托的任务上调用RunSynchronously.

InnerException:null

资料来源:mscorlib

StackTrace:

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
Run Code Online (Sandbox Code Playgroud)

Rac*_*hel 443

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

它可以使用:

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

代码来自这里

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 关于这是如何工作的一些背景,斯蒂芬图布(平行先生)写了一系列关于这个的帖子.[第1部分](http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx)[第2部分](http://blogs.msdn.com/b/pfxteam/ archive/2012/01/21/10259307.aspx)[第3部分](http://blogs.msdn.com/b/pfxteam/archive/2012/02/02/10263555.aspx) (26认同)
  • 我更新了John的代码,无需在lambda中包装任务:https://github.com/tejacques/AsyncBridge.基本上,您使用using语句处理异步块.使用块内的任何内容都是异步发生的,最后等待.缺点是您需要在回调中自己解开任务,但它仍然相当优雅,特别是如果您需要一次调用多个异步函数. (17认同)
  • @StephenCleary虽然我普遍同意你的看法,代码应该一直是异步的,但有时你发现自己处于一种不可行的情况,即*有*强制它作为同步调用.基本上,我的情况是我的所有数据访问代码都是异步的.我需要根据站点地图构建站点地图,我使用的第三方库是MvcSitemap.现在当一个人通过`DynamicNodeProviderBase`基类扩展它时,不能将它声明为`async`方法.要么我必须用新库替换,要么只调用同步操作. (17认同)
  • @ justin.lovell:是的,*库限制*可以强迫我们放入黑客,至少在库更新之前.听起来像MvcSitemap是需要黑客攻击的一种情况(MVC过滤器和子操作); 我只是劝阻人们,因为这样的黑客经常在他们不需要时使用.特别是对于MVC,一些ASP.NET/MVC API确实假设它们具有"AspNetSynchronizationContext",因此如果您调用这些API,这种特定的hack将无法工作. (5认同)
  • 此代码无效.如果从池线程调用它,它可以触发线程饥饿死锁.您的调用者将阻止等待操作完成,如果他已经耗尽了线程池,则可能永远不会发生.见[本文](http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx). (5认同)
  • 有时候你继承了别人写的所有同步的代码,你必须在某个截止日期之前交付一些东西,并且重写所有异步向上的东西都不是一种选择. (3认同)
  • 这段代码有一个错误。当抛出异常时,它不会恢复旧的上下文(`SynchronizationContext.SetSynchronizationContext(oldContext)`)。使用 `try { ... } finally { ... }` 修复它。 (3认同)
  • @ghord - 如果你不想阻塞 UI,那么你不能同步运行它......根据定义,它会阻塞它运行的线程。如果您不想阻止它,只需等待它。我不明白你问的重点。 (2认同)
  • @cameron macfarland 引用了 Stephen Toub 的一篇博客文章。它不起作用了。有人有更新的链接吗? (2认同)

AK_*_*AK_ 310

请注意,这个答案是三岁.我的写作主要基于.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();
Run Code Online (Sandbox Code Playgroud)

请参阅: TaskAwaiter, Task.Result, Task.RunSynchronously


.Net 4.0

用这个:

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

x.AsyncWaitHandle.WaitOne();
Run Code Online (Sandbox Code Playgroud)

...或这个:

task.Start();
task.Wait();
Run Code Online (Sandbox Code Playgroud)

  • 正如我在博客中描述的那样,`结果`可以[很容易导致`async`代码死锁](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). (117认同)
  • `.Result`在某些情况下会产生死锁 (64认同)
  • 该问题涉及异步方法返回的任务.这种任务可能已经启动,执行或取消,因此使用*Task.RunSynchronously*方法可能会导致*InvalidOperationException*.请参阅MSDN页面:[Task.RunSynchronously Method](http://msdn.microsoft.com/en-us/library/dd321435(v = vs.110).aspx).此外,该任务可能由*Task.Factory.StartNew*或*Task.Run*方法(在异步方法内)创建,因此尝试再次启动它是危险的.某些竞争条件可能在运行时发生.在另一方面,*Task.Wait*和*Task.Result*可能导致我死锁. (9认同)
  • @StephenCleary我看了你的帖子,并亲自尝试过.老实说,我认为微软的某个人真的喝醉了......这就像winforms和背景线程一样...... (8认同)
  • Run Synchronously为我工作...我不知道我是否遗漏了一些东西,但这似乎比标记答案的恐怖更好 - 我只是想找到一种方法来关闭异步测试代码,只是为了停止ui悬挂 (4认同)
  • A。可能无法在未绑定到委托的任务上调用 RunSynchronously,因此我将其包装在 Task(async () =&gt; wait methodAsync().ConfigureAwait(continueOnCapturedContext: false)) 中。根据 MSDN 文章添加了 .Wait() 令人惊讶的是,任务开始同步执行,然后在 I/O 开始时声明完成,导致令人困惑的竞争条件。又跌回到.GetAwaiter().GetResult(); 这确实等待完成。为什么 RunSynchronously() 和 Wait() 在异步任务真正完成之前返回??? (2认同)

Jam*_* Ko 116

惊讶没有人提到这个:

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

int result = BlahAsync().GetAwaiter().GetResult();
Run Code Online (Sandbox Code Playgroud)

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

  • 它不会吞下异常(例如Wait)
  • 它不会包装任何抛出的异常AggregateException(如Result)
  • 适用于两者TaskTask<T>(自己尝试!)

此外,由于GetAwaiter是duck-typed,这应该适用于从异步方法(如ConfiguredAwaitableYieldAwaitable)返回的任何对象,而不仅仅是任务.


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

// 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.
Run Code Online (Sandbox Code Playgroud)

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

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
Run Code Online (Sandbox Code Playgroud)

  • 请不要编辑其他人的答案以插入您自己的链接.如果您认为自己的答案更好,请将其留作评论. (19认同)
  • 这在asmx Web方法内部导致了死锁。不过,将方法调用包装在Task.Run()中可以使其起作用:Task.Run(()=&gt; BlahAsync())。GetAwaiter()。GetResult() (3认同)

Mic*_*rry 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; 
Run Code Online (Sandbox Code Playgroud)

  • @sgnsajgon你错了.Task.Run与Task.Factory.StartNew的不同之处在于它已经自动解包结果.见[本文](http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx). (4认同)
  • 然后你调用task.Wait().数据类型只是Task. (3认同)

Ste*_*ary 53

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

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

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

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

它是构造函数吗?那么您可能想要考虑异步工厂方法.(有关更多信息,请参阅我在异步构造函数上的博客文章).

几乎总是有一个比同步异步更好的答案.

如果你的情况不可能(你通过在这里问一个描述情况的问题就知道),那么我建议你只使用同步代码.一路异步是最好的; 一路同步是第二好的.建议不要使用同步异步.

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

在这种情况下,您需要使用我在棕色地带async开发文章中描述的其中一个黑客,具体来说:

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

嵌套的消息循环是所有黑客中最危险的,因为它会导致重新入侵.Re-entrancy非常难以理解,而且(IMO)是Windows上大多数应用程序错误的原因.特别是,如果你在UI线程上并且阻塞工作队列(等待异步工作完成),那么CLR实际上会为你做一些消息 - 它实际上会处理你内部的一些Win32消息代码.噢,你不知道哪些消息 - 当克里斯布鲁姆 说"难道不知道究竟会有什么东西会被抽出来吗?不幸的是,抽水是一种超乎凡理解的黑色艺术." 那么我们真的没有希望知道.

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

是的.用心写的代码,在重入面对强劲.嵌套的消息循环迫使您编写在重入时面对强大的代码.这就是为什么接受(和最upvoted)答案这个问题非常危险的做法.

如果你完全没有其他所有选项 - 你不能重新设计你的代码,你不能将它重组为异步 - 你可以通过不可更改的调用代码强制同步 - 你不能将下游代码更改为同步- 你不能阻止 - 你不能在一个单独的线程上运行异步代码 - 那么只有你才考虑采用重入.

如果你确实发现自己在这个角落,我会建议使用类似于Dispatcher.PushFrameWPF应用程序,循环使用Application.DoEventsWinForm应用程序,以及一般情况下,我自己的AsyncContext.Run.

  • 这个答案远远超过我的头脑.*"一直使用异步"*是令人困惑的建议,因为显然无法遵循.具有异步`Main()`方法的程序无法编译; 在某些时候,你*可以*弥合同步和异步世界之间的差距.这不是*"**非常罕见的情况"*,在字面上每个调用异步方法的程序都是必要的.没有选项可以不"*同步异步"*,只是一个选项,可以将负担分流到调用方法,而不是在你当前正在编写的方法中承担它. (7认同)
  • @AlexeiLevenkov:我觉得这样做不对,有几个原因:1)链接问题的答案相当过时。2)我写了一篇[关于该主题的整篇文章](https://msdn.microsoft.com/en-us/magazine/mt238404.aspx),我认为它比任何现有的 SO Q/A 更完整。3)这个问题的公认答案*非常*受欢迎。4)我“强烈”反对那个被接受的答案。因此,将其作为其重复内容而关闭将是滥用权力;将其作为此(或合并)的重复将导致更加危险的答案。我就顺其自然吧,把它留给社区。 (2认同)
  • @MarkAmery:控制台应用程序的“Main”方法中需要异步同步。ASP.NET、单元测试框架和每个 UI 系统都原生支持异步。即使您的所有应用程序都是控制台应用程序,您也只需为每个应用程序执行一次异步同步。(当然,不支持异步的库回调可能需要额外的技巧)。 (2认同)

Cle*_*ent 24

这对我很有用

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


The*_*ung 23

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

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

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

  1. 如果进入的线程await位于Dispatcher或WinForms消息循环线程上,则它确保异步方法的块作为消息队列处理的一部分发生.
  2. 如果进入的线程await在线程池线程上,则异步方法的剩余块发生在线程池的任何位置.

这就是为什么你可能遇到问题 - 异步方法实现试图在Dispatcher上运行其余部分 - 即使它已被暂停.

.... 备份!....

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

  1. [最佳] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing()- WPFWinForms代码
  6. [消息循环] - WPFWinForms消息循环

然后,一旦代码转换为使用异步,您通常会最终得到

  1. [最佳] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing()- WPFWinForms代码
  6. [消息循环] - WPFWinForms消息循环

实际上是回答

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

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

var customerList = TaskEx.RunEx(GetCustomers).Result;
Run Code Online (Sandbox Code Playgroud)

最终的API将是Task.Run(...),但是使用CTP你需要Ex后缀(这里有解释).


pix*_*xel 18

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

Task t = new Task(() => 
{ 
   //.... YOUR CODE ....
});
t.RunSynchronously();
Run Code Online (Sandbox Code Playgroud)

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


J. *_*non 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");
Run Code Online (Sandbox Code Playgroud)

它简单,容易,我也没有问题.


wen*_*nhx 15

我在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();
}
Run Code Online (Sandbox Code Playgroud)


Rre*_*Cat 12

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

Task<Customer> task = GetCustomers();
task.Wait()
Run Code Online (Sandbox Code Playgroud)

适用于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"
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 在异步情况下调用时,这很容易导致死锁. (2认同)

Dan*_*mov 9

在你的代码中,你的第一个等待任务执行但你还没有启动它所以它无限期地等待.试试这个:

Task<Customer> task = GetCustomers();
task.RunSynchronously();
Run Code Online (Sandbox Code Playgroud)

编辑:

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

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

        Assert.AreEqual (1, val);
}
Run Code Online (Sandbox Code Playgroud)

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

编辑#2:

我用Reflector检查了你描述的异常是什么时候发生m_actionnull.这有点奇怪,但我不是Async CTP的专家.就像我说的,你应该反编译你的代码,看看究竟是如何Task被实例化任何是怎么来m_actionnull.


PS什么是偶尔的downvotes处理?注意详细说明?

  • @gaearon我认为你已经得到了投票,因为你的帖子不适用于问题.讨论是关于异步等待方法,而不是简单的任务返回方法.此外,在我看来,async-await机制是一种语法糖,但不是那么微不足道 - 有继续,上下文捕获,本地上下文恢复,增强的本地异常处理等等.然后,您不应该对异步方法的结果调用*RunSynchronously*方法,因为根据定义,异步方法应该返回当前至少已调度的Task,并且多次处于运行状态. (2认同)

Dan*_*ite 6

为什么不创建一个如下调用:

Service.GetCustomers();
Run Code Online (Sandbox Code Playgroud)

那不是异步.

  • 如果我不能正常工作,这就是我的工作......除了Async版本之外,还要创建一个Sync版本 (3认同)

Mah*_*esh 5

使用下面的代码片段

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
Run Code Online (Sandbox Code Playgroud)

  • Task.WaitAll 在这里没有添加任何内容。为什么不等一下呢? (2认同)

Lia*_*ang 5

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

对于异步方法return Task

Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();
Run Code Online (Sandbox Code Playgroud)

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

Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;
Run Code Online (Sandbox Code Playgroud)

编辑

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

  • 将近 8 年后我的回答:) 第二个例子 - 将在主要使用的所有预定上下文中产生死锁(控制台应用程序/.NET 核心/桌面应用程序/...)。在这里你有更多的概述我现在在说什么:https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d (2认同)

Jai*_*der 5

注意:我认为,如果它是异步的,最好的做法是不建议更改操作的性质,最好的做法是按原样处理(一直异步)。通过这种方式,您可以获得其他好处,例如并行处理/多线程等。

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

var customers = GetCustomersAsync().GetAwaiter().GetResult();
Run Code Online (Sandbox Code Playgroud)