Любое преимущество для асинхронного метода, который синхронизирует цикл - PullRequest
0 голосов
/ 09 ноября 2018

Я пишу программу, которая должна извлекать данные из внешнего API через SDK. Я не очень хорошо разбираюсь в операциях await / async - но знаю, что SDK для каждого из вызовов предлагает метод Async. GetResultsAsync<TObject>()

У каждого запроса есть предел возвращаемых записей - так, чтобы получить все записи за месяц - я должен упорядочить по идентификатору и выполнить цикл. Вот пример кода текущих синхронных вызовов. Пока я получаю результаты - я проверяю больше.

public List<TimeEntry> ListTimeEntries(DateTime startDate, DateTime endDate)
{
    var page = 1;
    var hasMore = false;
    var qry = string.Format("timeStart >= [{0}] and timeEnd <= [{1}]", startDate, endDate); //For example
    var results = List<TimeEntry>();
    do
    {
        var newItems = api.GetEntries(qry, "id desc", maxPageSize, page).GetResults<List<TimeEntry>>();
        results.AddRange(newItems);
        hasMore = newItems.Any();
        page++;
    } while (hasMore);
    return results;
}

Это может составить 4-5 циклов (или больше в зависимости от дат), но, очевидно, отнимает много времени.

Если я просто преобразую это в метод async - смогу ли я одновременно выполнять другие синхронные вызовы? Давайте предположим, что мне также нужно получить служебные и проектные билеты за один и тот же период времени, но не выполняйте никакой логической обработки, пока все не будут возвращены. Могу ли я сделать все три вызова асинхронными?

Вот цель

var startDate = DateTime.Now.AddDays(-30);
var endDate = DateTime.Now;

var timeTask = ListTimeEntries(startDate, endDate);
var serviceTask = ListServiceTickets(startDate, endDate);
var projectTask = ListProjectTickets(startdDate, endDate);

var timeEntries = await timeTask;
var serviceTickets = await serviceTask;
var projectTickets = await projectTask;

// Do what ever processing logic I need now.

Как достичь?

Для пример # 1 - просто ли заставить Метод работать как async? EG:

public async Task<List<TimeEntry>> ListTimeEntries(DateTime startDate, DateTime endDate)
{
    var page = 1;
    var hasMore = false;
    var qry = string.Format("timeStart >= [{0}] and timeEnd <= [{1}]", startDate, endDate); //For example
    var results = List<TimeEntry>();
    do
    {
        var newItems = api.GetEntries(qry, "id desc", maxPageSize, page).GetResults<List<TimeEntry>>();
        results.AddRange(newItems);
        hasMore = newItems.Any();
        page++;
    } while (hasMore);
    return results;
}

Или было бы полезно использовать метод SDK GetResultsAsync<>, такой как Пример 2 :

public async Task<List<TimeEntry>> ListTimeEntries(DateTime startDate, DateTime endDate)
{
    var page = 1;
    var hasMore = false;
    var qry = string.Format("timeStart >= [{0}] and timeEnd <= [{1}]", startDate, endDate); //For example
    var results = List<TimeEntry>();
    do
    {
        var newItemsTask = api.GetEntries(qry, "id desc", maxPageSize, page).GetResultsAsync<List<TimeEntry>>();
        var newItems = await newItemsTask;
        results.AddRange(newItems);
        hasMore = newItems.Any();
        page++;
    } while (hasMore);
    return results;
}

Хотя я понимаю, что эти методы внутренне все еще работают синхронно - я надеюсь, что каждый вызов к ListTimeEntries, ListServiceTickets и ListProjectTickets происходит асинхронно для ускорения.

Полагаю, моя мысль / надежда - из-за недостатка знаний - заключалась бы в том, что при каждом синхронном вызове использовался бы отдельный поток, что ускоряло бы весь процесс. Я далеко от базы?

1 Ответ

0 голосов
/ 09 ноября 2018

Я бы сделал что-то вроде следующего. Вам нужно будет знать о потенциальных проблемах, связанных с ограничениями API, и, конечно, есть возможность получить по крайней мере три дополнительных запроса в процессе до завершения всех 4 потоков - небольшая цена, которую нужно заплатить, если у вас есть много страниц для извлечения.

public async Task<IList<TimeEntry>> CallSomething(DateTime startDate, DateTime endDate)
{
    var qry = string.Format("timeStart >= [{0}] and timeEnd <= [{1}]", startDate, endDate); //For example

    const threadCount = 4;

    var isComplete = new ManualResetEvent();
    var page = 0;
    var results = new ConcurrentBag<TimeEntry>();
    var thread = new Task[threadCount];
    for(var i = 0; i < threadCount; ++i)
    {
        thread[i] = Task.Run(
            async () =>
            {
                while(!isComplete.WaitOne(TimeSpan.Zero))
                {
                    var localPage = Interlocked.Increment(ref page);

                    var newItems = await api
                        .GetEntries(qry, "id desc", maxPageSize, localPage)
                        .GetResultsAsync<List<TimeEntry>>()
                        .ConfigureAwait(false);

                    if (!newItems.Any())
                    {
                        isComplete.Set();
                    }
                    else
                    {
                        foreach (var item in newItems)
                        {
                            results.Add(item);
                        }
                    }
                }
            })
        )
    }

    await Task.WhenAll(thread).ConfigureAwait(false);

    return results.OrderByDescending(f => f.Id).ToList();
}
...