同步执行异步function
我已就此主题进行了大量搜索,并且我在此网站上阅读了有关此主题的大部分post,但我仍然感到困惑,我需要一个直接的答案。 这是我的情况:
我有一个已建立的Winform应用程序,我无法将其全部“异步”。 我现在被迫使用一个全部写为异步函数的外部库。
在我的申请中,我有
/// /// This function I can't change it to an 'async' /// public void MySyncFunction() { //This function is my point in my application where I have to call the //other 'async' functions but I can't change the function itself to 'async' try { //I need to call the MyAsyncDriverFunction() as if it is a synchronous function //I need the driver function to finish execution and return before processing the code that follows it //I also need to be able to catch any exceptions MyAsyncDriverFunction(); //Rest of the code have to wait for the above function to return } catch (Exception exp) { //Need to be able to handle the exception thrown //from the MyAsyncDriverFunction here. } } public static async Task<IEnumerable> MyAsyncDriverFunction() { try { var strCollection = await AsyncExternalLibraryFunction1(); var strCollection2 = await AsyncExternalLibraryFunction2(); return strCollection; } catch (Exception exp) { //Need to be able to catch an exception and re-throw it to the caller function } }
如代码中所述,我需要能够:
- 我无法将MySyncFunction更改为异步
- 以同步方式调用“MyAsyncDriverFunction”,在处理后面的代码之前必须等待它完成所有工作
- 能够处理两个函数中的exception(从我到目前为止读到的这个很棘手吗?)
- 我需要一个使用标准API的简单方法,我不能使用任何第三方库(即使我想)
但我仍然感到困惑,我需要一个直接的答案。
那是因为没有“直截了当”的答案 。
唯一合适的解决方案是使MySyncFunction
异步。 期。 所有其他解决方案都是黑客攻击,并且没有任何黑客在所有场景中都能完美运行 。
我在最近关于棕色字段异步开发的MSDN文章中详细介绍了,但这里有一个要点:
您可以使用Wait()
或Result
进行阻止。 正如其他人所指出的那样,您很容易导致死锁,但如果异步代码永远不会在其捕获的上下文中恢复,则可以使用此方法。
您可以将工作推送到线程池线程,然后阻止。 但是,这假设异步工作能够被推送到其他任意线程,并且它可以在其他线程上恢复,因此可能引入multithreading。
您可以将工作推送到执行“主循环”的线程池线程 – 例如,调度程序或我自己的AsyncContext
类型。 这假设异步工作能够被推送到另一个线程,但消除了对multithreading的任何担忧。
您可以在主线程上安装嵌套的消息循环。 这将在调用线程上执行异步代码,但也会引入重入,这是非常难以正确推理的。
简而言之, 没有一个答案 。 每种方法都适用于不同类型的异步代码。
简单地针对您的异步方法调用.Result
或.Wait
将会死锁,因为您处于GUI应用程序的上下文中。 有关一个很好的解释,请参阅https://msdn.microsoft.com/en-us/magazine/jj991977.aspx(“Async All the Way”一章)。
问题的解决方案并不容易,但Stephen Cleary详细描述了这里 :
所以你应该使用Nito.AsyncEx库(在Nuget上可用 )。 如果你真的无法添加他写入你的项目的库,你可以检查源代码并使用它的一部分,MIT许可证允许它。
只需在方法调用结束时添加.Result
调用即可。
var strCollection = MyAsyncDriverFunction().Result;
我不确定专家们会说些什么,但根据Stephen Cleary的建议,我最终得到了以下想法。 有以下课程
public sealed class AsyncTask { public static void Run(Func asyncFunc) { var originalContext = SynchronizationContext.Current; bool restoreContext = false; try { if (originalContext != null && originalContext.GetType() != typeof(SynchronizationContext)) { restoreContext = true; SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); } var task = asyncFunc(); task.GetAwaiter().GetResult(); } finally { if (restoreContext) SynchronizationContext.SetSynchronizationContext(originalContext); } } public static TResult Run(Func> asyncFunc) { var originalContext = SynchronizationContext.Current; bool restoreContext = false; try { if (originalContext != null && originalContext.GetType() != typeof(SynchronizationContext)) { restoreContext = true; SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); } var task = asyncFunc(); return task.GetAwaiter().GetResult(); } finally { if (restoreContext) SynchronizationContext.SetSynchronizationContext(originalContext); } } }
并按如下方式使用它
public void MySyncFunction() { try { AsyncTask.Run(() => MyAsyncDriverFunction()); } catch (Exception exp) { } }
会做你没有陷入僵局的要求。 关键点是在异步任务执行期间“隐藏”当前同步上下文并强制使用已知为Post
方法使用线程池的默认同步上下文。 同样,我不确定这是好是坏,它可以引入什么副作用,但一旦你问,我只是分享它。
尝试将“await AsyncExternalLibraryFunction1()”更改为“AsyncExternalLibraryFunction1()。Wait()”并在其旁边,并删除函数“MyAsyncDriverFunction”的异步
- ServiceStack Ormlite:System.InvalidProgramException JIT编译器遇到内部限制
- Windowsapp store中的密码哈希
- Windows窗体应用程序中的任务并行库代码冻结 – 作为Windows控制台应用程序正常工作
- LogicalOperationStack与.Net 4.5中的异步不兼容
- 包含System.IO.Compression 时,当前上下文中不存在名称“ZipFile”
- 使用asyn和await的.net 4.5中的异步TCP服务器,正在发生数据丢失
- .NET 4.5 HttpClient请求ServicePoint
- Howto:Parallel.Foreach在每个进程运行一个新进程(但一次只有一个进程)后执行许多进程?
- C#generics类中的协方差