异步功能同步运行,而不是异步运行

时间:2020-04-15 18:50:13

标签: c# asynchronous async-await task

我阅读了这篇文章,并试图做同样的https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

我正在遍历ODATA中的一组实体,并尝试同时处理所有实体(功能Load())。 我的控制台应用程序看起来像这样

public static async Task Main(string[] args)
{
    //List<string> dataareaidlist = new List<string> {  "cvl", "cpm", "hk", "ivp",
    //    "jp", "kr", "me", "na", "sh", "sp", "spb", "spec", "spfr", "spit", "spiv",
    //    "spuk", "tw", "vgh", "wec", "wge" };
    List<string> dataareaidlist = new List<string> { "CAIN", "CPM", "dat", "CGV",
        "HK", "JP", "MAL", "ME", "NA", "CVL", "SIN", "SPFR", "SP", "SPIV", "SPIT",
        "SPUK", "SPB", "THA", "WEC", "WGE", "TWH", "TW", "KR", "SPEC", "IVP", "MSHK",
        "MSSH", "VGH", "SH" };
    System.Console.WriteLine("Start program");
    List<Load> Entities = new List<Load>();
    DateTime startprogram = DateTime.Now;
    Entities.Add(new Load("SalesInvoiceLines", dataareaidlist));
    Entities.Add(new Load("SalesOrderLines", dataareaidlist));

    List<Task<String>> listTaskLoad = new List<Task<String>>();
    foreach (Load load in Entities)
    {
        Entity entity = new Entity(resourceUrl, load.Entity, connection,
            accessToken, load.dataareaidlist);
        Task<String> t = entity.Load();
        listTaskLoad.Add(t);
    }

    while (listTaskLoad.Any())
    {
        Task<String> finished = await Task.WhenAny(listTaskLoad);
        System.Console.WriteLine("Entity finished:" + finished.Result.ToString());
        listTaskLoad.Remove(finished);
        finished = null;
    }
}

加载功能异步

public async Task<String> Load()
{
    System.Console.WriteLine("Start Load for entity:" + EntityName);
    //  GetNumberEntities();
    AnalyzeOdataLoad();
    TruncateStg();
    if (this.dataareaidlist != null)
    {

        Parallel.ForEach(this.dataareaidlist, (datareaid) =>
        {
            String url = resourceUrl + "/data/" + EntityName +
                "?$filter=dataAreaId eq '" + datareaid + "'&cross-company=true";
            Extract(url, 0, 0, 0);
        });
    }
    else
    {
        String url = resourceUrl + "/data/" + EntityName + "?cross-company=true";
        Extract(url, 0, 0, 0);
    }

    while (listTaskWrite.Any())
    {
        Task<Guid> finished = await Task.WhenAny(listTaskWrite);
        System.Console.WriteLine(" Task finished:" + finished.Result.ToString());
        listTaskWrite.Remove(finished);
        finished = null;
    }
    return this.EntityName;
}

因此,在主程序中,循环启动函数Load()一次开始(并等待整个Load()函数完成,然后再启动下一个),而不是立即启动所有Load() 。不知道为什么它不能一次全部启动。我在extract函数中使用了相同的登录名来加载db异步,并且工作正常。

希望对本教程有所帮助的任何线索或链接。

欢呼

Vincent

1 个答案:

答案 0 :(得分:1)

似乎您将异步与并行混淆了。

  • 异步的意思是“当另一件事发生时,停止运行我的代码,让当前线程在其他地方工作”
  • 并行的意思是“同时运行这两段(或更多)代码”(这需要多个线程)

异步方法是常规方法:调用它们时,它们与其他方法一样在同一线程上运行。魔术发生在作用于不完整await的第一个Task上。此时,await返回它本身是不完整的Task,并在Task完成后注册其余方法以完成操作。

在您的情况下,当您呼叫entity.Load()时,entity.Load()的运行就像直到第一个async都未标记为await一样。唯一的await位于await Task.WhenAny(listTaskWrite),但是您没有显示listTaskWrite的填充方式,因此可能永远不会命中该代码。但是无论如何,await之前的所有内容都会正常运行,就好像它不是async方法一样。

您有两种选择。如果要在Extract()中发出网络请求,则可以更改该代码以使其异步(HttpClient中的所有方法都是异步的),然后摆脱Parallel.ForEach。这样,它将启动HTTP请求,并在等待答复时开始处理下一个请求。所有这些都可能只使用一个线程完成,但是您已经用尽了它-它永远不会只是坐在那里等待答复。那是对资源的最佳利用。看起来像这样(假设您将Extract()修改为异步的,并且您想为此目的使用listTaskWrite

if (this.dataareaidlist != null)
{
    String url = resourceUrl + "/data/" + EntityName +
            "?$filter=dataAreaId eq '" + datareaid + "'&cross-company=true";
    listTaskWrite.Add(Extract(url, 0, 0, 0));
}
else
{
    String url = resourceUrl + "/data/" + EntityName + "?cross-company=true";
    listTaskWrite.Add(Extract(url, 0, 0, 0));
}

还可以启动并行作业(在另一个线程上运行代码)并异步等待。为此,您将使用Task.Run。例如:

Task<String> t = Task.Run(() => entity.Load());

这将在新线程(“并行”)上运行entity.Load(),并且您可以使用t来知道它何时完成。但是,您将必须谨慎使用此类代码。您正在为每个项目启动一个新线程,然后使用Parellel.ForEach,这将创建更多线程。在ASP.NET中,这是个坏主意,因为ASP.NET具有有限数量的可用线程。但是即使在任何其他类型的应用程序中,这些也是很多启动的线程。您可能会发现这样做的性能(完成时间)更糟。