715

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

异步方法:

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

System.InvalidOperationException

消息:不能在未绑定到委托的任务上调用 RunSynchronously。

内部异常:空

来源:mscorlib

堆栈跟踪

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

26 回答 26

513

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

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

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;
        }
    }
}
于 2011-02-23T21:02:36.343 回答
350

请注意,这个答案是三年前的。我主要根据使用 .Net 4.0 的经验编写它,很少使用 4.5,尤其是使用 .Net 4.0 async-await。一般来说,这是一个很好的简单解决方案,但它有时会破坏一些东西。请阅读评论中的讨论。

.Net 4.5

只需使用这个:

// For Task<T>: will block until the task is completed...
var result = task.Result; 

// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();

请参阅: TaskAwaiterTask.ResultTask.RunSynchronously


.Net 4.0

用这个:

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

x.AsyncWaitHandle.WaitOne();

...或这个:

task.Start();
task.Wait();
于 2012-08-02T17:03:47.100 回答
163

很惊讶没有人提到这一点:

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

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

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

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

此外,由于是鸭子类型,这应该适用于从异步方法(如or )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()
于 2016-02-07T19:29:54.563 回答
78

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

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; 
于 2013-06-13T18:50:54.853 回答
69

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

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

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

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

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

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

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

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

在这种情况下,您需要使用我关于棕地async开发的文章中描述的一种技巧,特别是:

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

嵌套消息循环是所有黑客中最危险的,因为它会导致重新进入。重新进入非常难以推理,并且(IMO)是 Windows 上大多数应用程序错误的原因。特别是,如果你在 UI 线程上并且你阻塞在一个工作队列上(等待异步工作完成),那么 CLR 实际上会为你做一些消息泵送——它实际上会处理你内部的一些 Win32 消息代码。哦,你不知道哪些信息 - 当Chris Brumme 说“确切地知道会抽什么不是很好吗?不幸的是,抽水是一种超出凡人理解的魔法。” ,那我们真的没有希望知道了。

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

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

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

如果您确实发现自己在这个角落,我建议您使用类似Dispatcher.PushFramefor WPF apps,looping with Application.DoEventsfor WinForm 应用程序,对于一般情况,我自己的AsyncContext.Run.

于 2016-09-07T13:40:33.810 回答
26

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

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

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

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

这就是您可能遇到问题的原因——异步方法实现试图在 Dispatcher 上运行其余的方法——即使它已暂停。

.... 备份!……

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

  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;

最终的 API 将是 Task.Run(...),但对于 CTP,您将需要 Ex 后缀(这里有解释)。

于 2011-02-24T21:29:15.890 回答
23

这对我来说效果很好

public static class TaskHelper
{
    public static void RunTaskSynchronously(this Task t)
    {
        var task = Task.Run(async () => await t);
        task.Wait();
    }

    public static T RunTaskSynchronously<T>(this Task<T> t)
    {
        T res = default(T);
        var task = Task.Run(async () => res = await t);
        task.Wait();
        return res;
    }
}
于 2014-07-23T05:42:37.700 回答
17

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

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

于 2013-10-05T02:54:50.803 回答
17

我在 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();
}
于 2014-12-02T02:06:48.140 回答
17

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

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

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

于 2016-10-25T23:22:45.307 回答
17

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

对于异步方法返回Task.

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

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

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

编辑

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

于 2018-10-24T09:08:51.280 回答
13

请注意 - 这种方法:

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 的评论)

于 2012-06-20T22:10:20.490 回答
9

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

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

编辑:

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

[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_actionnull

于 2011-02-23T18:23:15.087 回答
6

为什么不创建如下调用:

Service.GetCustomers();

那不是异步的。

于 2011-02-23T18:20:34.367 回答
5

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

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

var customers = GetCustomersAsync().GetAwaiter().GetResult();
于 2020-11-16T20:34:20.073 回答
4

使用下面的代码片段

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
于 2016-10-19T04:13:30.440 回答
4

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

通过异步方法获取值:

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

同步调用异步方法

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

不会因为使用Task.Run.

来源:

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

于 2017-09-25T13:49:42.240 回答
3

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

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

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

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

/// <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();
        }
    }
}
于 2014-09-12T17:41:19.000 回答
2

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

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);
于 2016-02-16T09:58:37.270 回答
1

这对我有用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    public static class AsyncHelper
    {
        private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

        public static void RunSync(Func<Task> func)
        {
            _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }

        public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        {
            return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }
    }

    class SomeClass
    {
        public async Task<object> LoginAsync(object loginInfo)
        {
            return await Task.FromResult(0);
        }
        public object Login(object loginInfo)
        {
            return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
            //return this.LoginAsync(loginInfo).Result.Content;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var someClass = new SomeClass();

            Console.WriteLine(someClass.Login(1));
            Console.ReadLine();
        }
    }
}
于 2018-07-27T15:20:06.293 回答
-1

我知道这是一个老问题,但我想分享我的解决方案,可能不是最好的,但可以完成工作:

//Declare an Event Handler:
private event EventHandler OnThemeApply;

//..somewhere in your code..

//we 'hear' the event handler
this.OnThemeApply += (object _Sender, EventArgs _E) =>
{
  //Reaches here After the Async method had finished
  
  this.OnThemeApply = null;
};
MyAsycMethod();

private void MyAsycMethod()
{
   var t = System.Threading.Tasks.Task.Factory.StartNew(delegate
   {
      //Do something here

      Invoke((MethodInvoker)(() =>
      {
         if(this.OnThemeApply != null) this.OnThemeApply(null, null); //<- Calls the Event
      }));
   });
}
于 2022-01-28T19:55:28.583 回答
-2

我发现 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()。它还允许您指定超时,这样您就不会永远卡在任务永远无法完成的情况下。

于 2019-02-06T22:02:41.097 回答
-3

在 wp8 上:

把它包起来:

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

称它为:

GetCustomersSynchronously();
于 2013-02-26T23:13:36.460 回答
-4
    private int GetSync()
    {
        try
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            int result = null;

            Parallel.Invoke(async () =>
            {
                result = await SomeCalcAsync(5+5);
                mre.Set();
            });

            mre.WaitOne();
            return result;
        }
        catch (Exception)
        {
            return null;
        }
    }
于 2013-09-27T20:29:16.237 回答
-6

或者你可以选择:

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

为此,请确保您引用扩展程序集:

System.Net.Http.Formatting
于 2016-03-08T23:21:17.980 回答
-9

尝试以下对我有用的代码:

public async void TaskSearchOnTaskList (SearchModel searchModel)
{
    try
    {
        List<EventsTasksModel> taskSearchList = await Task.Run(
            () => MakeasyncSearchRequest(searchModel),
            cancelTaskSearchToken.Token);

        if (cancelTaskSearchToken.IsCancellationRequested
                || string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
        {
            return;
        }

        if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
        {
            RunOnUiThread(() => {
                textViewNoMembers.Visibility = ViewStates.Visible;                  
                taskListView.Visibility = ViewStates.Gone;
            });

            taskSearchRecureList = null;

            return;
        }
        else
        {
            taskSearchRecureList = TaskFooterServiceLayer
                                       .GetRecurringEvent(taskSearchList);

            this.SetOnAdapter(taskSearchRecureList);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
    }
}
于 2015-09-03T07:39:35.043 回答