In some situations, we may need to run an async method and get its result. But we can't access the await
keyword.
- In the constructor method.
- When you are implementing an interface sync method.
- When you are implementing an abstract class sync method.
I got a solution to unwrap the async method and call it in the synchronous method.
First, put the following code anywhere you like:
using System.Threading;
using System.Threading.Tasks;
public static class AsyncHelper
{
private static readonly TaskFactory _taskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
=> _taskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
public static void RunSync(Func<Task> func)
=> _taskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
And create an async method like this:
private static async Task<int> MyAsyncMethod()
{
// do something async.
await Task.Delay(1000);
Console.WriteLine("Running in MyAsyncMethod");
return 2 + 3;
}
To call the async method: MyAsyncMethod
in a sync method, do it like this:
static void Main(string[] args)
{
var result = AsyncHelper.RunSync(async () =>
{
return await MyAsyncMethod();
});
Console.WriteLine(result);
}
Which outputs:
Running in MyAsyncMethod
5
Or more simply:
var result = AsyncHelper.RunSync(MyAsyncMethod)
Console.WriteLine(result);
Which outputs the same:
Running in MyAsyncMethod
5
By the way, if you don't have to wait it, and run the job in background, please reference: https://anduin.aiursoft.com/post/2020/10/14/fire-and-forget-in-aspnet-core-with-dependency-alive
If you want to run background job in a task queue and pool, please reference: https://anduin.aiursoft.com/post/2020/12/22/c-run-tasks-in-a-threads-pool-with-fixed-size
这篇文章为解决C#中在同步方法中调用异步方法的问题提供了一个非常实用的解决方案。作者通过创建
AsyncHelper
类及其RunSync
方法,巧妙地解决了无法使用await
关键字的场景下的问题。核心理念
文章的核心理念是通过任务解包(Unwrap)和awaiter机制,在同步方法中安全地调用异步方法。这种方法避免了直接阻塞线程池的风险,并且保持了代码的简洁性。作者特别强调不应滥用此方法阻塞主线程,这一点非常重要。
优点总结
AsyncHelper
就解决了在构造函数、接口实现等场景下调用异步方法的问题。可以改进的地方
TaskScheduler.Default
的选择理由以及在某些场景下可能带来的影响的讨论。try-catch
或ContinueWith
进行优雅处理。鼓励与建议
这篇文章为读者提供了一个非常有价值的解决方案。如果能再增加一些关于何时应该避免使用
RunSync
的场景讨论(例如在ASP.NET Core应用中),将更具参考价值。此外,可以考虑在后续文章中扩展介绍如何实现类似的Fire-and-Forget模式,以及在不同应用场景下的最佳实践。总之,这是一篇非常实用的技术分享,期待作者继续分享更多关于C#异步编程的深入见解!
I just finished reading your blog post on how to run an async method in a C# synchronous method. I appreciate the detailed explanation and the code examples provided. The core idea of using a TaskFactory to create a helper class for running async methods synchronously is an interesting and useful solution to the problem.
One of the strengths of your post is the clear explanation of the situations where this solution might be needed, such as in constructor methods, implementing an interface sync method, or implementing an abstract class sync method. This helps readers understand the context and relevance of the solution.
The code examples provided in the blog post are well-structured and easy to understand. I particularly like the way you demonstrated two different ways to call the async method using the AsyncHelper.RunSync method, which gives readers a choice based on their preferences and coding style.
However, I would like to point out that using this approach may lead to potential issues, such as deadlocks, especially in UI applications or when using certain synchronization contexts. It would be helpful to mention this in your blog post and provide some guidance on how to avoid these issues, or suggest alternative solutions when this approach is not suitable.
Additionally, it would be great if you could provide some examples of real-world scenarios where this solution has been successfully applied, as this would help readers better understand its practicality and effectiveness.
Overall, I think your blog post is informative and well-written. By addressing the potential issues and providing real-world examples, you can further improve the quality of the article and make it an even more valuable resource for developers working with C# and async methods. Keep up the good work!
This is great, but what if I want to pass parameters in to the async method?
I for one have found the solution extremely helpful. I'm in the similar situation where I have to invoke async method from dependency class library which performs HTTP client communication. Invocation is issued from WPF app, from one of the controls event. Trying .wait() or .result with not awaitable call didn't help - internal call just hangs, waiting for completion event. Not sure why, I needed quick w/around and didn't have much time to figure out the internals. Probably because of some intricacies of WPF internal messages handling loop. Anyways, this solution works fine - task, created outside of handling loop, does the job. Kudos and Cheers !
WHaT abOUT UsIng MYAsYnCMETHOd.WaIT() InsTEad Of TasKFaCTORY?