Asyn c функция запускается в syn c вместо async - PullRequest
0 голосов
/ 15 апреля 2020

Я прочитал эту статью и пытался сделать то же самое 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;
    }
}

Функция загрузки asyn c

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;
}

Итак, в основной программе l oop для запуска функции Load () начните один за другим (и дождитесь, пока вся функция Load () завершится sh, прежде чем запускать следующую), вместо того, чтобы запускать всю Load () сразу. Не уверен, почему это не начинается все сразу. Я использую тот же логин внутри функции извлечения, чтобы загрузить db asyn c, и он прекрасно работает.

Буду признателен за любую подсказку или ссылку на учебник, который поможет мне понять.

Cheers

Винсент

1 Ответ

1 голос
/ 16 апреля 2020

Похоже, что вы путаете асинхронный с параллельным.

  • Асинхронный означает "пока это происходит, прекратите выполнение моего кода и позвольте текущему потоку go работать над чем-то другим"
  • Параллельный означает «запустить эти два (или более) фрагмента кода одновременно» (для этого требуется более одного потока)

Асинхронные методы - это обычные методы: когда вы их вызываете они работают в том же потоке, как и любой другой метод. Маги c происходят в первый 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 имеет ограниченное количество доступных потоков. Но даже в любом другом приложении запускается множество потоков. Вы можете обнаружить, что при этом производительность (время до финиша sh) хуже.

...