我将如何运行异步任务方法同步?

发布于 2024-10-19 01:30:29 字数 5648 浏览 9 评论 0 原文

我正在学习 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)

我还尝试了 此处,但是当调度程序处于挂起状态时它不起作用。

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 的异常和堆栈跟踪:

系统.InvalidOperationException

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

内部异常:空

来源: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()

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

Async method:

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

Normal usage:

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

I've tried using the following:

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

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

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

I also tried a suggestion from here, however it doesn't work when the dispatcher is in a suspended state.

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

Here is the exception and stack trace from calling RunSynchronously:

System.InvalidOperationException

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

InnerException: null

Source: mscorlib

StackTrace:

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(23

ˇ宁静的妩媚 2024-10-26 01:30:29

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

可以使用以下方式调用它:

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

代码来自 此处

public static class AsyncHelpers
{
    /// <summary>
    /// Synchronously execute's an async Task method which has a void return value.
    /// </summary>
    /// <param name="task">The Task method to execute.</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var syncContext = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(syncContext);
        
        syncContext.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                syncContext.InnerException = e;
                throw;
            }
            finally
            {
                syncContext.EndMessageLoop();
            }
        }, null);
        
        syncContext.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Synchronously execute's an async Task<T> method which has a T return type.
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">The Task<T> method to execute.</param>
    /// <returns>The result of awaiting the given Task<T>.</returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var syncContext = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(syncContext);
        T result;
        
        syncContext.Post(async _ =>
        {
            try
            {
                result = await task();
            }
            catch (Exception e)
            {
                syncContext.InnerException = e;
                throw;
            }
            finally
            {
                syncContext.EndMessageLoop();
            }
        }, null);
        
        syncContext.BeginMessageLoop();
        
        SynchronizationContext.SetSynchronizationContext(oldContext);
        
        return result;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        private readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();
        private bool done;
        
        public Exception InnerException { get; set; }

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

Here's a workaround I found that works for all cases (including suspended dispatchers). It's not my code and I'm still working to fully understand it, but it does work.

It can be called using:

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

Code is from here

public static class AsyncHelpers
{
    /// <summary>
    /// Synchronously execute's an async Task method which has a void return value.
    /// </summary>
    /// <param name="task">The Task method to execute.</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var syncContext = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(syncContext);
        
        syncContext.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                syncContext.InnerException = e;
                throw;
            }
            finally
            {
                syncContext.EndMessageLoop();
            }
        }, null);
        
        syncContext.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Synchronously execute's an async Task<T> method which has a T return type.
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">The Task<T> method to execute.</param>
    /// <returns>The result of awaiting the given Task<T>.</returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var syncContext = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(syncContext);
        T result;
        
        syncContext.Post(async _ =>
        {
            try
            {
                result = await task();
            }
            catch (Exception e)
            {
                syncContext.InnerException = e;
                throw;
            }
            finally
            {
                syncContext.EndMessageLoop();
            }
        }, null);
        
        syncContext.BeginMessageLoop();
        
        SynchronizationContext.SetSynchronizationContext(oldContext);
        
        return result;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        private readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();
        private bool done;
        
        public Exception InnerException { get; set; }

        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;
        }
    }
}
对不⑦ 2024-10-26 01:30:29

请注意这个答案已有三年了。我主要根据 .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();

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


.Net 4.0

使用此:

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

x.AsyncWaitHandle.WaitOne();

...或此:

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

Be advised this answer is three years old. I wrote it based mostly on a experience with .Net 4.0, and very little with 4.5 especially with async-await.
Generally speaking it's a nice simple solution, but it sometimes breaks things. Please read the discussion in the comments.

.Net 4.5

Just use this:

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

See:
TaskAwaiter,
Task.Result,
Task.RunSynchronously


.Net 4.0

Use this:

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

x.AsyncWaitHandle.WaitOne();

...or this:

task.Start();
task.Wait();
梦幻的味道 2024-10-26 01:30:29

令人惊讶的是没有人提到这一点:

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

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

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

  • 它不会吞咽异常(如 Wait
  • 它不会包装抛出的任何异常AggregateException (如 Result
  • 适用于 TaskTask (try it out yourself!)

Also, since GetAwaiter 是鸭子类型的,这应该适用于从异步方法返回的任何对象(例如 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.

如果你懒得到处添加 .ConfigureAwait(false) ,并且你不关心性能,你也可以这样做

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

Surprised no one mentioned this:

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

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

Not as pretty as some of the other methods here, but it has the following benefits:

  • it doesn't swallow exceptions (like Wait)
  • it won't wrap any exceptions thrown in an AggregateException (like Result)
  • works for both Task and Task<T> (try it out yourself!)

Also, since GetAwaiter is duck-typed, this should work for any object that is returned from an async method (like ConfiguredAwaitable or YieldAwaitable), not just Tasks.


edit: Please note that it's possible for this approach (or using .Result) to deadlock, unless you make sure to add .ConfigureAwait(false) every time you await, for all async methods that can possibly be reached from BlahAsync() (not just ones it calls directly). 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.

If you're too lazy to add .ConfigureAwait(false) everywhere, and you don't care about performance you can alternatively do

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
铁轨上的流浪者 2024-10-26 01:30:29

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

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; 

It's much simpler to run the task on the thread pool, rather than trying to trick the scheduler to run it synchronously. That way you can be sure that it won't deadlock. Performance is affected because of the context switch.

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; 
撑一把青伞 2024-10-26 01:30:29

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

最好的答案是你不需要,具体细节取决于“情况”是什么。

它是属性获取器/设置器吗?在大多数情况下,拥有异步方法比“异步属性”更好。 (有关详细信息,请参阅我关于异步属性的博客文章)。

这是一个 MVVM 应用程序并且您想要进行异步数据绑定吗?然后使用类似于我的 NotifyTask 的内容,如我的 有关异步数据绑定的 MSDN 文章

它是一个构造函数吗?那么您可能需要考虑异步工厂方法。 (有关详细信息,请参阅我的关于异步构造函数的博客文章)。

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

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

然而,在少数情况下,异步同步是必要的。具体来说,您受到调用代码的限制,因此您必须要同步(并且绝对没有办法重新思考或重新构造代码以允许异步),必须调用异步代码。这是一种非常罕见的情况,但确实时有发生。

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

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

嵌套消息循环是所有黑客攻击中最危险的,因为它会导致重入 。重新进入是非常难以推理的,并且(IMO)是 Windows 上大多数应用程序错误的原因。特别是,如果您位于 UI 线程上并且阻塞工作队列(等待异步工作完成),那么 CLR 实际上会为您执行一些消息泵操作 - 它实际上会处理一些 Win32 消息从您的代码中。哦,你不知道哪些消息 - 当 Chris Brumme 说“如果能准确地知道会抽吸什么不是很好吗?不幸的是,抽吸是一门超出凡人理解范围的黑术。”,然后我们真的没有希望知道。

因此,当您在 UI 线程上像这样阻塞时,您就是在自找麻烦。同一篇文章中的另一个 cbrumme 引用:“公司内部或外部的客户有时会发现我们在 STA [UI 线程] 上的托管阻塞期间泵送消息。这是一个合理的担忧,因为他们知道这非常困难编写面对重入时稳健的代码。”

是的。 非常很难编写在重入面前保持健壮的代码。嵌套消息循环迫使您编写面对重入时稳健的代码。这就是为什么这个问题被接受的(也是投票最多的)答案极其危险实践。

如果您完全没有其他选择 - 您无法重新设计代码,无法将其重组为异步 - 您被迫通过不可更改的调用代码来同步 - 您无法将下游代码更改为同步- 你不能阻塞 - 你不能在单独的线程上运行异步代码 - 然后并且只有这样你才应该考虑拥抱重入。

如果您确实发现自己处于这个角落,我建议使用类似 Dispatcher.PushFrame 对于 WPF 应用程序,对于 WinForm 应用程序使用 Application.DoEvents 循环,对于一般情况,我自己的 AsyncContext.Run

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?

The best answer is you don't, with the details dependent on what the "situation" is.

Is it a property getter/setter? In most cases, it's better to have asynchronous methods than "asynchronous properties". (For more info, see my blog post on asynchronous properties).

Is this an MVVM app and you want to do asynchronous data binding? Then use something like my NotifyTask, as described in my MSDN article on asynchronous data binding.

Is it a constructor? Then you probably want to consider an asynchronous factory method. (For more info, see my blog post on asynchronous constructors).

There's almost always a better answer than to do sync-over-async.

If it's not possible for your situation (and you know this by asking a question here describing the situation), then I'd recommend just using synchronous code. Async all the way is best; sync all the way is second-best. Sync-over-async is not recommended.

However, there are a handful of situations where sync-over-async is necessary. Specifically, you are constrained by the calling code so that you have to be sync (and have absolutely no way to re-think or re-structure your code to allow asynchrony), and you have to call async code. This is a very rare situation, but it does come up from time to time.

In that case, you would need to use one of the hacks described in my article on brownfield async development, specifically:

  • Blocking (e.g., GetAwaiter().GetResult()). Note that this can cause deadlocks (as I describe on my blog).
  • Running the code on a thread pool thread (e.g., Task.Run(..).GetAwaiter().GetResult()). Note that this will only work if the asynchronous code can be run on a thread pool thread (i.e., is not dependent on a UI or ASP.NET context).
  • Nested message loops. Note that this will only work if the asynchronous code only assumes a single-threaded context, not a specific context type (a lot of UI and ASP.NET code expect a specific context).

Nested message loops are the most dangerous of all the hacks, because it causes re-entrancy. Re-entrancy is extremely tricky to reason about, and (IMO) is the cause of most application bugs on Windows. In particular, if you're on the UI thread and you block on a work queue (waiting for the async work to complete), then the CLR actually does some message pumping for you - it'll actually handle some Win32 messages from within your code. Oh, and you have no idea which messages - when 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.", then we really have no hope of knowing.

So, when you block like this on a UI thread, you're asking for trouble. Another cbrumme quote from the same article: "From time to time, customers inside or outside the company discover that we are pumping messages during managed blocking on an STA [UI thread]. This is a legitimate concern, because they know that it’s very hard to write code that’s robust in the face of reentrancy."

Yes, it is. Very hard to write code that's robust in the face of reentrancy. And nested message loops force you to write code that's robust in the face of reentrancy. This is why the accepted (and most-upvoted) answer for this question is extremely dangerous in practice.

If you are completely out of all other options - you can't redesign your code, you can't restructure it to be async - you are forced by unchangeable calling code to be sync - you can't change the downstream code to be sync - you can't block - you can't run the async code on a separate thread - then and only then should you consider embracing reentrancy.

If you do find yourself in this corner, I would recommend using something like Dispatcher.PushFrame for WPF apps, looping with Application.DoEvents for WinForm apps, and for the general case, my own AsyncContext.Run.

初见 2024-10-26 01:30:29

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

C# 5 中的异步方法是通过在底层有效地将方法分成多个部分,并返回一个可以跟踪整个 shabang 整体完成情况的 Task 来提供支持的。但是,切碎的方法的执行方式可能取决于传递给 await 运算符的表达式的类型。

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

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

这就是为什么您可能会遇到问题 - 异步方法实现正在尝试在调度程序上运行其余部分 - 即使它已挂起。

....备份! ....

我必须问一个问题,为什么 你试图同步阻塞异步方法?这样做会违背为什么要异步调用该方法的目的。一般来说,当您开始在 Dispatcher 或 UI 方法上使用 await 时,您将希望将整个 UI 流程设为异步。例如,如果您的调用堆栈如下所示:

  1. [Top] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode .AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI 代码].Plumbing() - WPFWinForms< /code> 代码
  6. [消息循环] - WPFWinForms 消息循环

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

  1. [Top] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. < code>YourCode.EventHandlerMethodAsync()
  5. [UI 代码].Plumbing() - WPFWinForms 代码
  6. [Message Loop] - WPFWinForms 消息循环

实际应答

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

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

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

最终的 API 将是 Task.Run(...),但使用 CTP,您将需要 Ex 后缀 (此处有说明)。

If I am reading your question right - the code that wants the synchronous call to an async method is executing on a suspended dispatcher thread. And you want to actually synchronously block that thread until the async method is completed.

Async methods in C# 5 are powered by effectively chopping the method into pieces under the hood, and returning a Task that can track the overall completion of the whole shabang. However, how the chopped up methods execute can depend on the type of the expression passed to the await operator.

Most of the time, you'll be using await on an expression of type Task. Task's implementation of the await pattern is "smart" in that it defers to the SynchronizationContext, which basically causes the following to happen:

  1. If the thread entering the await is on a Dispatcher or WinForms message loop thread, it ensures that the chunks of the async method occurs as part of the processing of the message queue.
  2. If the thread entering the await is on a thread pool thread, then the remaining chunks of the async method occur anywhere on the thread pool.

That's why you're probably running into problems - the async method implementation is trying to run the rest on the Dispatcher - even though it's suspended.

.... backing up! ....

I have to ask the question, why are you trying to synchronously block on an async method? Doing so would defeat the purpose on why the method wanted to be called asynchronously. In general, when you start using await on a Dispatcher or UI method, you will want to turn your entire UI flow async. For example, if your callstack was something like the following:

  1. [Top] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing() - WPF or WinForms Code
  6. [Message Loop] - WPF or WinForms Message Loop

Then once the code has been transformed to use async, you'll typically end up with

  1. [Top] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing() - WPF or WinForms Code
  6. [Message Loop] - WPF or WinForms Message Loop

Actually Answering

The AsyncHelpers class above actually works because it behaves like a nested message loop, but it installs its own parallel mechanic to the Dispatcher rather than trying to execute on the Dispatcher itself. That's one workaround for your problem.

Another workaround is to execute your async method on a threadpool thread, and then wait for it to complete. Doing so is easy - you can do it with the following snippet:

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

The final API will be Task.Run(...), but with the CTP you'll need the Ex suffixes (explanation here).

若无相欠,怎会相见 2024-10-26 01:30:29

这对我来说效果很好

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

This is working well for me

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;
    }
}
撩发小公举 2024-10-26 01:30:29

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

对于返回 Task 的异步方法。

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

对于返回 Task

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

Edit 的异步方法:

如果调用者运行在线程池线程中(或者调用者也在任务中),仍然可能会导致在某些情况下陷入僵局。

Tested in .Net 4.6. It can also avoid deadlock.

For async method returning Task.

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

For async method returning Task<T>

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

Edit:

If the caller is running in the thread pool thread (or the caller is also in a task), it may still cause a deadlock in some situation.

冰火雁神 2024-10-26 01:30:29

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

I found this code at Microsoft.AspNet.Identity.Core component, and it works.

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();
}
微暖i 2024-10-26 01:30:29

我遇到过几次,主要是在单元测试或 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");

它简单、容易,而且我没有遇到任何问题。

I've faced it a few times, mostly in unit testing or in a windows service development. Currently I always use this feature:

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

It's simple, easy and I had no problems.

情未る 2024-10-26 01:30:29

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

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

在我的例子中,我有一个在发生某些事情时触发的事件。我不知道这样的事情会发生多少次。因此,我在事件中使用上面的代码,因此每当它触发时,它都会创建一个任务。任务是同步执行的,这对我来说非常有用。我只是很惊讶,考虑到它是如此简单,我花了这么长时间才发现它。通常,建议要复杂得多并且容易出错。这就是它的简单和干净。

The simplest way I have found to run task synchronously and without blocking UI thread is to use RunSynchronously() like:

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

In my case, I have an event that fires when something occurs. I dont know how many times it will occur. So, I use code above in my event, so whenever it fires, it creates a task. Tasks are executed synchronously and it works great for me. I was just surprised that it took me so long to find out of this considering how simple it is. Usually, recommendations are much more complex and error prone. This was it is simple and clean.

游魂 2024-10-26 01:30:29

请注意 - 此方法:

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的注释)

Just a little note - this approach:

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

works for WinRT.

Let me explain:

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

Moreover this approach works for Windows Store solutions only!

Note: This way isn't thread safe if you call your method inside of other async method (according to comments of @Servy)

你曾走过我的故事 2024-10-26 01:30:29

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

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

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

NOTE: I think as best practice is not recommended to change the nature of the action if it is asynchronous the best thing is handling as it is (async all the way). In that way you can get other benefits like parallel processing / multi-threading, etc.

Seeing the other answers did not use this approach, I want to post it here as well:

var customers = GetCustomersAsync().GetAwaiter().GetResult();
耳根太软 2024-10-26 01:30:29

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

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

编辑:

您说您遇到了例外情况。请发布更多详细信息,包括堆栈跟踪。
单声道 包含以下测试用例:

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

        Assert.AreEqual (1, val);
}

检查这是否适合您。如果没有,尽管可能性很小,您可能会使用一些奇怪的异步 CTP 版本。如果它确实有效,您可能需要检查编译器到底生成了什么以及任务实例化与此示例有何不同。

编辑#2:

我向 Reflector 核实,当 m_actionnull 时,会发生您所描述的异常。这有点奇怪,但我不是异步 CTP 方面的专家。正如我所说,您应该反编译代码并查看 Task 是如何实例化的,以及它的 m_action 为何为 null

In your code, your first wait for task to execute but you haven't started it so it waits indefinitely. Try this:

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

Edit:

You say that you get an exception. Please post more details, including stack trace.
Mono contains the following test case:

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

        Assert.AreEqual (1, val);
}

Check if this works for you. If it does not, though very unlikely, you might have some odd build of Async CTP. If it does work, you might want to examine what exactly the compiler generates and how Task instantiation is different from this sample.

Edit #2:

I checked with Reflector that the exception you described occurs when m_action is null. This is kinda odd, but I'm no expert on Async CTP. As I said, you should decompile your code and see how exactly Task is being instantiated any how come its m_action is null.

无言温柔 2024-10-26 01:30:29

为什么不创建一个像这样的调用:

Service.GetCustomers();

这不是异步的。

Why not create a call like:

Service.GetCustomers();

that isn't async.

泪痕残 2024-10-26 01:30:29

此答案是为使用 WPF for .NET 4.5 的任何人设计的。

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

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

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

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

This answer is designed for anyone who is using WPF for .NET 4.5.

If you attempt to execute Task.Run() on the GUI thread, then task.Wait() will hang indefinitely, if you do not have the async keyword in your function definition.

This extension method solves the problem by checking to see if we are on the GUI thread, and if so, running the task on the WPF dispatcher thread.

This class can act as the glue between the async/await world and the non-async/await world, in situations where it is unavoidable, such as MVVM properties or dependencies on other APIs that do not use async/await.

/// <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();
        }
    }
}
注定孤独终老 2024-10-26 01:30:29

正如许多人在评论中所说,简单地调用 .Result;.Wait() 就会带来死锁的风险。由于我们大多数人都喜欢 oneliner,因此您可以将它们用于 .Net 4.5<

通过异步方法获取值:

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

同步调用异步方法

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

由于使用 Task 不会出现死锁问题.运行

来源:

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

Simply calling .Result; or .Wait() is a risk for deadlocks as many have said in comments. Since most of us like oneliners you can use these for .Net 4.5<

Acquiring a value via an async method:

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

Syncronously calling an async method

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

No deadlock issues will occur due to the use of Task.Run.

Source:

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

鹿港小镇 2024-10-26 01:30:29

使用下面的代码片段

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

use below code snip

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
也只是曾经 2024-10-26 01:30:29

这对我有用

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

This is works for me

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();
        }
    }
}
瑾兮 2024-10-26 01:30:29

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

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

I think the following helper method could also solve the problem.

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

Can be used the following way:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
小帐篷 2024-10-26 01:30:29

异步方法:

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

这似乎对我有用:

Task.Run(GetCustomers).Wait();

Async method:

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

This seems to work for me:

Task.Run(GetCustomers).Wait();
じее 2024-10-26 01:30:29

我发现 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()。它还允许您指定超时,以便在任务永远无法完成的情况下您不会永远陷入困境。

I have found that SpinWait works pretty well for this.

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;

The above approach doesn't need to use .Result or .Wait(). It also lets you specify a timeout so that you're not stuck forever in case the task never completes.

祁梦 2024-10-26 01:30:29
    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;
        }
    }
    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;
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文