【问题标题】:stop bubbling up Async await Task to caller without getting deadlock停止向调用者冒泡异步等待任务而不会出现死锁
【发布时间】:2018-01-05 15:35:38
【问题描述】:

我有一个使用异步函数 GetParametersByPathAsync 的库(在此处定义:https://github.com/aws/aws-sdk-net/blob/master/sdk/src/Services/SimpleSystemsManagement/Generated/_mobile/AmazonSimpleSystemsManagementClient.cs#L2718

我有一个库函数定义为

Task<Dictionary<string,string>> GetAllParameters(string region)
{
    var pars = DoParameterGatheringWork(reigion);
    ...(do some more work)
    return dict;
}

调用另一个方法

async Task<Dictionary<string,string>> DoParameterGatheringWork(string region)
{
    ...
    var response = await GetParametersByPathAsync(requestObj);
    ... (process the response)
    return parameterDict;
}

GetParametersByPathAsync 上等待并收集东西。

这是一个问题,因为GetAllParameters 必须由我的服务从静态构造函数调用并初始化参数Dictionary&lt;string,string&gt; MyParameters { get; }

我想在库中的某个位置停止这种冒泡的任务,因此它可以只公开Dictionary&lt;string,string&gt; GetAllParameters(string region),而不是任务版本。 (我对它变得同步完全没问题..)

我认为我不应该只做Task.Wait()Task.Result,因为这会导致死锁。

也许这不是最好的方法,但我不确定如何从这里继续。

任何想法将不胜感激!

编辑:

这是我想要的构造函数代码:

public class MyConfiguration
{
    static MyConfiguration()
    {
        ...
        Parameters = ServiceConfiguration.GetAllParameters(); // (library call)
    }
    public static Dictionary<string, string> Parameters { get; }
}

客户只需通过MyConfiguration.Parameters["IAMAPARAMETER"]就可以在任何地方使用它

【问题讨论】:

  • 听起来你在找Async Initialization
  • 所以你只想同步调用一个异步函数?
  • @DavidG 是的。但不会遇到死锁问题。
  • @JSteward 这看起来不错。我会调查一下谢谢
  • @smbl 为什么你想停止冒泡?同步意味着阻塞。 阻塞 会浪费 CPU,因为它通常在使线程进入睡眠状态之前从 spinwaiting 开始。尤其是在云场景中,您不会阻塞或烧毁 CPU 什么都不做。现在甚至控制台应用程序都有async Task Main()

标签: c# asynchronous async-await task


【解决方案1】:

评论后:在这个答案的最后:如何从非异步方法调用异步方法

显然DoParameterGatheringWork 是一个函数,通常需要忙于等待另一个进程,例如数据库、文件或来自 Internet 的一些信息。

该函数的设计者认为,如果您的线程闲置等待此删除操作的结果,那将是浪费时间。因此他决定让它异步,这样调用者就可以做其他事情,而其他进程会处理请求。

你没看错,这意味着所有调用者也应该是异步的,而构造函数不能是异步的。

如果您想从 async-await 的优势中受益(这意味着您的调用者可以继续处理而不是无所事事地等待,请使您的构造函数轻量级并让某些 Create 函数完成您通常在构造函数中执行的异步工作。强制每个希望你的类的对象使用这个异步创建函数的人。

public class MyConfiguration
{
    // Static async Create function, does everything you'd normally do in the constructor:
    public static async Task<MyConfiguration> CreateAsync()
    {
        Dictionary<string,string> allParameters = await ServiceConfiguration.GetAllParameters(...);
        MyConfiguration createdConfiguration = new MyConfiguration(allParameters);
        return createdConfiguration;
     }

     // make sure that no one except CreateAsync can call the constructor:
     private MyConfiguration(Dictionary<string,string> allParameters)
     {
          parameters = allParameters;
     }
}

您所做的是使构造函数尽可能轻量级,并完成所有困难的工作,包括 CreateAsync 函数中的 await。

用法:

以下会导致编译错误,所以你在开始运行之前就知道问题所在:

MyConfiguration config = new MyConfiguration(...);

正确使用:

async Task<...> DoSomethingAsync(...)
{
     ...
     // I need a configuration:
     MyConfiguration config = await MyConfiguration.Create();
     // from here you can use the fully initialized MyConfiguration object:
     config.DoSomethingElse();
     ...
}

简单的祝你好运

补充:如何从非异步函数调用异步方法

要从非异步方法调用异步函数,请使用Task.Run 启动异步函数,Task.Wait 等待异步函数完成,Task.Result 获取异步函数的返回值。

static void Main(string[] args)
{
    // call an async function:
    var asyncTask = Task.Run( () => MyAsyncFunction(...));

    // if desired: do other things
    DoSomethingElse();

    // now you need the result of the async function
    asyncTask.Wait();
    var returnValue = asyncTask.Result;
    Process(returnvalue);
}

【讨论】:

  • 差不多。这将传递私有构造函数,但在应用程序的最开始,Startup.cs 的 ConfigureServices 方法中将需要此配置类。而且这个方法不能是异步的。据我所知,这是我在调用链中可以达到的最低点(或最高点,无论你怎么看)..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多