我有一个public async void Foo()方法,我想从同步方法中调用它。 到目前为止,我从MSDN文档中看到的所有内容都是通过异步方法调用异步方法,但是我的整个程序不是使用异步方法构建的。

这有可能吗?

这是从异步方法调用这些方法的一个示例: http : //msdn.microsoft.com/zh-cn/library/hh300224(v=vs.110).aspx

现在,我正在研究从同步方法调用这些异步方法。


#1楼

我不确定100%,但是我相信此博客中描述的技术在许多情况下都可以使用:

因此,如果要直接调用此传播逻辑,则可以使用task.GetAwaiter().GetResult()


#2楼

   //Example from non UI thread -    private void SaveAssetAsDraft(){SaveAssetDataAsDraft();}private async Task<bool> SaveAssetDataAsDraft(){var id = await _assetServiceManager.SavePendingAssetAsDraft();return true;   }//UI Thread - var result = Task.Run(() => SaveAssetDataAsDraft().Result).Result;

#3楼

Microsoft建立了一个AsyncHelper(内部)类来将Async作为Sync运行。 源看起来像:

internal static class AsyncHelper
{private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);public static TResult RunSync<TResult>(Func<Task<TResult>> func){return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(func).Unwrap<TResult>().GetAwaiter().GetResult();}public static void RunSync(Func<Task> func){AsyncHelper._myTaskFactory.StartNew<Task>(func).Unwrap().GetAwaiter().GetResult();}
}

Microsoft.AspNet.Identity基类仅具有Async方法,为了将它们称为Sync,有些类的扩展方法如下所示(示例用法):

public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{if (manager == null){throw new ArgumentNullException("manager");}return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{if (manager == null){throw new ArgumentNullException("manager");}return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}

对于那些关心代码许可条款的人来说,这里是指向非常相似的代码的链接(只是增加了对线程的区域性的支持),并带有注释以表明它已获得MIT的Microsoft许可。 https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs


#4楼

异步Main现在是C#7.2的一部分,可以在项目高级构建设置中启用。

对于C#<7.2,正确的方法是:

static void Main(string[] args)
{MainAsync().GetAwaiter().GetResult();
}static async Task MainAsync()
{/*await stuff here*/
}

您会在许多Microsoft文档中看到它,例如: https : //docs.microsoft.com/zh-cn/azure/service-bus-messaging/service-bus-dotnet-how-to-use-主题-订阅


#5楼

这些Windows异步方法有一个漂亮的小方法,称为AsTask()。 您可以使用它使该方法作为任务返回自身,以便您可以在其上手动调用Wait()。

例如,在Windows Phone 8 Silverlight应用程序上,您可以执行以下操作:

private void DeleteSynchronous(string path)
{StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();t.Wait();
}private void FunctionThatNeedsToBeSynchronous()
{// Do some work here// ....// Delete something in storage synchronouslyDeleteSynchronous("pathGoesHere");// Do other work here // .....
}

希望这可以帮助!


#6楼

最被接受的答案并不完全正确。 有一种适用于各种情况的解决方案:即席消息泵(SynchronizationContext)。

调用线程将按预期方式被阻止,同时仍确保从异步函数调用的所有连续都不会死锁,因为它们将被封送到调用线程上运行的临时SynchronizationContext(消息泵)中。

临时消息泵助手的代码:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;namespace Microsoft.Threading
{/// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>public static class AsyncPump{/// <summary>Runs the specified asynchronous method.</summary>/// <param name="asyncMethod">The asynchronous method to execute.</param>public static void Run(Action asyncMethod){if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");var prevCtx = SynchronizationContext.Current;try{// Establish the new contextvar syncCtx = new SingleThreadSynchronizationContext(true);SynchronizationContext.SetSynchronizationContext(syncCtx);// Invoke the functionsyncCtx.OperationStarted();asyncMethod();syncCtx.OperationCompleted();// Pump continuations and propagate any exceptionssyncCtx.RunOnCurrentThread();}finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }}/// <summary>Runs the specified asynchronous method.</summary>/// <param name="asyncMethod">The asynchronous method to execute.</param>public static void Run(Func<Task> asyncMethod){if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");var prevCtx = SynchronizationContext.Current;try{// Establish the new contextvar syncCtx = new SingleThreadSynchronizationContext(false);SynchronizationContext.SetSynchronizationContext(syncCtx);// Invoke the function and alert the context to when it completesvar t = asyncMethod();if (t == null) throw new InvalidOperationException("No task provided.");t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);// Pump continuations and propagate any exceptionssyncCtx.RunOnCurrentThread();t.GetAwaiter().GetResult();}finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }}/// <summary>Runs the specified asynchronous method.</summary>/// <param name="asyncMethod">The asynchronous method to execute.</param>public static T Run<T>(Func<Task<T>> asyncMethod){if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");var prevCtx = SynchronizationContext.Current;try{// Establish the new contextvar syncCtx = new SingleThreadSynchronizationContext(false);SynchronizationContext.SetSynchronizationContext(syncCtx);// Invoke the function and alert the context to when it completesvar t = asyncMethod();if (t == null) throw new InvalidOperationException("No task provided.");t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);// Pump continuations and propagate any exceptionssyncCtx.RunOnCurrentThread();return t.GetAwaiter().GetResult();}finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }}/// <summary>Provides a SynchronizationContext that's single-threaded.</summary>private sealed class SingleThreadSynchronizationContext : SynchronizationContext{/// <summary>The queue of work items.</summary>private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();/// <summary>The processing thread.</summary>private readonly Thread m_thread = Thread.CurrentThread;/// <summary>The number of outstanding operations.</summary>private int m_operationCount = 0;/// <summary>Whether to track operations m_operationCount.</summary>private readonly bool m_trackOperations;/// <summary>Initializes the context.</summary>/// <param name="trackOperations">Whether to track operation count.</param>internal SingleThreadSynchronizationContext(bool trackOperations){m_trackOperations = trackOperations;}/// <summary>Dispatches an asynchronous message to the synchronization context.</summary>/// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>/// <param name="state">The object passed to the delegate.</param>public override void Post(SendOrPostCallback d, object state){if (d == null) throw new ArgumentNullException("d");m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));}/// <summary>Not supported.</summary>public override void Send(SendOrPostCallback d, object state){throw new NotSupportedException("Synchronously sending is not supported.");}/// <summary>Runs an loop to process all queued work items.</summary>public void RunOnCurrentThread(){foreach (var workItem in m_queue.GetConsumingEnumerable())workItem.Key(workItem.Value);}/// <summary>Notifies the context that no more work will arrive.</summary>public void Complete() { m_queue.CompleteAdding(); }/// <summary>Invoked when an async operation is started.</summary>public override void OperationStarted(){if (m_trackOperations)Interlocked.Increment(ref m_operationCount);}/// <summary>Invoked when an async operation is completed.</summary>public override void OperationCompleted(){if (m_trackOperations &&Interlocked.Decrement(ref m_operationCount) == 0)Complete();}}}
}

用法:

AsyncPump.Run(() => FooAsync(...));

有关异步泵的更多详细说明,请参见此处 。


#7楼

添加最终解决了我的问题的解决方案,希望可以节省一些时间。

首先阅读Stephen Cleary的几篇文章:

  • 异步并等待
  • 不要阻塞异步代码

根据“不要阻止异步代码”中的“两个最佳实践”,第一个不适用于我,第二个不适用于(基本上,如果我可以使用await ,我可以!)。

所以这是我的解决方法:将调用包装在Task.Run<>(async () => await FunctionAsync()); 希望不再有僵局

这是我的代码:

public class LogReader
{ILogger _logger;public LogReader(ILogger logger){_logger = logger;}public LogEntity GetLog(){Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());return task.Result;}public async Task<LogEntity> GetLogAsync(){var result = await _logger.GetAsync();// more code here...return result as LogEntity;}
}

#8楼

var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();

或使用此:

var result=result.GetAwaiter().GetResult().AccessToken

#9楼

对于任何关注这个问题的人来说...

如果您查看Microsoft.VisualStudio.Services.WebApi则有一个名为TaskExtensions的类。 在该类中,您将看到静态扩展方法Task.SyncResult() ,它就像完全阻塞线程一样,直到任务返回。

它在内部调用非常简单的task.GetAwaiter().GetResult() ,但是它可以重载以处理返回TaskTask<T>Task<HttpResponseMessage> ...语法糖,宝贝...爸爸的任何async方法爱吃甜食。

看起来...GetAwaiter().GetResult()是在阻塞上下文中执行异步代码的MS官方方法。 对于我的用例来说似乎工作得很好。


#10楼

我知道我来晚了。 但是,如果像我这样的人想要以一种整洁,简单的方式解决此问题,而又不必依赖其他库。

我从Ryan找到了以下代码

public static class AsyncHelpers
{private static readonly TaskFactory taskFactory = newTaskFactory(CancellationToken.None,TaskCreationOptions.None,TaskContinuationOptions.None,TaskScheduler.Default);/// <summary>/// Executes an async Task method which has a void return value synchronously/// USAGE: AsyncUtil.RunSync(() => AsyncMethod());/// </summary>/// <param name="task">Task method to execute</param>public static void RunSync(Func<Task> task)=> taskFactory.StartNew(task).Unwrap().GetAwaiter().GetResult();/// <summary>/// Executes an async Task<T> method which has a T return type synchronously/// USAGE: T result = AsyncUtil.RunSync(() => AsyncMethod<T>());/// </summary>/// <typeparam name="TResult">Return Type</typeparam>/// <param name="task">Task<T> method to execute</param>/// <returns></returns>public static TResult RunSync<TResult>(Func<Task<TResult>> task)=> taskFactory.StartNew(task).Unwrap().GetAwaiter().GetResult();
}

那么你可以这样称呼它

var t = AsyncUtil.RunSync<T>(() => AsyncMethod<T>());

#11楼

如果要运行它同步

MethodAsync().RunSynchronously()

#12楼

您可以从同步代码中调用任何异步方法,也就是说,直到需要await它们时,在这种情况下,它们也必须标记为async

正如很多人在这里建议的那样,您可以在同步方法中对结果任务调用Wait()或Result,但是最后在该方法中导致阻塞调用,这使异步的目的无效。

我真的不能使您的方法async并且您不想锁定同步方法,那么您将不得不通过将回调方法作为参数传递给任务上的ContinueWith方法来使用回调方法。


#13楼

public async Task<string> StartMyTask()
{await Foo()// code to execute once foo is done
}static void Main()
{var myTask = StartMyTask(); // call your method which will return control once it hits await// now you can continue executing code herestring result = myTask.Result; // wait for the task to complete to continue// use result}

您将关键字“ await”读为“启动此长期运行的任务,然后将控制权返回给调用方法”。 长时间运行的任务完成后,它将在其后执行代码。 等待之后的代码类似于以前的CallBack方法。 最大的区别在于逻辑流程不会被打断,这使得写入和读取变得更加容易。


#14楼

异步编程确实在代码库中“增长”。 它已经被比作僵尸病毒 。 最好的解决方案是允许它增长,但是有时这是不可能的。

我在Nito.AsyncEx库中编写了一些类型,用于处理部分异步的代码库。 但是,没有一种解决方案可以在每种情况下都适用。

解决方案A

如果您有一个简单的异步方法不需要同步回到其上下文,则可以使用Task.WaitAndUnwrapException

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

希望使用Task.WaitTask.Result因为包装在异常AggregateException

仅当MyAsyncMethod不同步回到其上下文时,此解决方案才适用。 换句话说, MyAsyncMethod每个await都应以ConfigureAwait(false)结尾。 这意味着它无法更新任何UI元素或访问ASP.NET请求上下文。

解决方案B

如果MyAsyncMethod确实需要同步回其上下文,则可以使用AsyncContext.RunTask提供嵌套的上下文:

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

* 2014年4月14日更新:在该库的最新版本中,API如下:

var result = AsyncContext.Run(MyAsyncMethod);

(这是确定使用Task.Result在这个例子中,因为RunTask将传播Task除外)。

您可能需要AsyncContext.RunTask而不是Task.WaitAndUnwrapException的原因是由于WinForms / WPF / SL / ASP.NET上发生相当微妙的死锁:

  1. 同步方法调用异步方法,获得Task
  2. 同步方法对Task进行阻塞等待。
  3. async方法使用await而不使用ConfigureAwait
  4. 在这种情况下, Task无法完成,因为它仅在async方法完成时才完成; async方法无法完成,因为它正在尝试安排其继续到SynchronizationContext ,并且WinForms / WPF / SL / ASP.NET将不允许继续运行,因为同步方法已在该上下文中运行。

这就是为什么最好在每个async方法中都使用ConfigureAwait(false)原因之一。

解决方案C

AsyncContext.RunTask并非在每种情况下都起作用。 例如,如果async方法等待需要完成UI事件的操作,那么即使使用嵌套上下文,您也将死锁。 在这种情况下,您可以在线程池上启动async方法:

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

但是,此解决方案需要MyAsyncMethod ,它将在线程池上下文中工作。 因此,它无法更新UI元素或访问ASP.NET请求上下文。 在这种情况下,您最好将ConfigureAwait(false)添加到其await语句中,并使用解决方案A。

更新,2019-05-01: MSDN文章在此处提供了当前的“最差实践”。

如何在C#中从同步方法调用异步方法?相关推荐

  1. 一码阻塞,万码等待:ASP.NET Core 同步方法调用异步方法“死锁”的真相

    在我们 2015 年开始的从 .NET Framework 向 .NET Core 迁移的工程中,遇到的最大的坑就是标题中所说的--同步方法中调用异步方法发生"死锁".虽然在 .N ...

  2. async await 同步方法调用异步方法死锁

    同步方法调用异步方法 .GetAwaiter().GetResult() 计算函数超时,异步方法所有的回调操作都会期望返回到主线程. 所以会导致各种线程死锁.异步方法中使用ConfigureAwait ...

  3. 如何在Clion中使用C++调用Python代码

    在很多时候,我们需要在一个c++工程项目中调用部分Python代码,这就需要我们实现Python和C++之间的交互.交互方式有两种:1. 依靠 TCP 建立的网络通信交互:2. 嵌入式混合语言编程(E ...

  4. matlab怎么调用filter函数,如何在matlab中建立和调用filter函数

    数字滤波是Matlab信号处理中的常用功能.具体步骤为: step1:设计好滤波器系数,b和a.其中b,a存储了滤波器响应传递函数的分子和分母系数. step2:产生输入信号x step3:实现滤波y ...

  5. java 执行代码超时,如何在Java中使用超时调用一些阻塞方法?

    Is there a standard nice way to call a blocking method with a timeout in Java? I want to be able to ...

  6. channelread0会被调用两次_值得一看:C#同步方法中如何调用异步方法?

    前言 我在写代码的时候(.net core)有时候会碰到void方法里,调用async方法并且Wait,而且我还看到别人这么写了.而且我这么写的时候,编译器没有提示任何警告.但是看了dudu的文章:一 ...

  7. 又踩.NET Core的坑:在同步方法中调用异步方法Wait时发生死锁(deadlock)

    之前在将 Memcached 客户端 EnyimMemcached 迁移 .NET Core 时被这个"坑"坑的刻骨铭心(详见以下链接),当时以为只是在构造函数中调用异步方法(注: ...

  8. java同步调用异步方法_关于同步方法里面调用异步方法的探究

    ##前言 我在写代码的时候(.net core)有时候会碰到void方法里,调用async方法并且Wait,而且我还看到别人这么写了.而且我这么写的时候,编译器没有提示任何警告.但是看了dudu的文章 ...

  9. flutter调用api_如何在Flutter(REST API)中进行API调用

    flutter调用api 在本文中,我们将看一下如何快速进行API调用并使用简单的REST API. 在这里查看我在Flutter上的其他一些帖子: Flutter vs React Native 了 ...

最新文章

  1. __declspec(dllexport)的作用
  2. python开发闹钟_「玩转树莓派」为女朋友打造一款智能语音闹钟
  3. 软件工程结对作业 四则运算界面设计
  4. d3 line example debug 2015-05-31
  5. 教育管理系统——android家长客户端
  6. dd命令打包多个文件_linux的tar命令详情;linux多个文件压缩打包到一个压缩文件...
  7. 鸿蒙系统的升级名单,定档6月2日!鸿蒙“首批”升级名单公布,共计11款华为机型!...
  8. MTK 驱动开发(31)---Sensor 移植及调试1
  9. ghost方式批量安装win7
  10. 类和对象9:属性访问方法
  11. python同时含有大写小写_如何使python同时加密大写和小写?
  12. Html5面试问题总结(精华)
  13. 中国生活垃圾处理行业十四五规划与投融资模式分析报告2022~2028年
  14. Ubuntu 18.04安装openJDK7编译安卓6.0.0_r1
  15. 个人信息保护相关的重要法规及规范性文件汇总目录
  16. 51单片机NTC热敏电阻温度采集及控制ADC0832 LCD1602
  17. echarts 提示框被挡住如何解决
  18. 下载USGS的地物光谱数据库的网址
  19. 基于单片机节日彩灯控制器系统设计、基于单片机脉搏检测和体温检测设计、基于单片机可燃气体检测及报警系统、基于单片机可调八路抢答器仿真设计-全套设计资料
  20. word仿宋字体自动变成仿宋GB2312的解决方案(转)

热门文章

  1. D7控件\dw_cd_VirtualTreeview_v4.5.2\Demos\Advanced---TVirtualStringTree用法
  2. Rsync服务配置详解,实现服务器间数据同步!
  3. ASP.NET Identity系列01,揭开神秘面纱
  4. 使用docker方式安装etcd集群,带TLS证书
  5. vue axios封装以及登录token过期跳转问题
  6. web前端基础之JS
  7. UNITY 之FixedUpdate
  8. Codeforces Beta Round #7 C. Line 扩展欧几里德
  9. 设计模式(七): 通过转接头来观察适配器模式(Adapter Pattern)
  10. Android Fragment 真正彻底的解决(下一个)