异步循环调用的异步方法的任何好处

时间:2018-11-09 16:39:51

标签: c# loops asynchronous async-await

我正在编写一个必须通过SDK从外部api提取数据的程序。我不太熟悉await / async操作-但是知道每个调用的SDK提供了Async方法。 GetResultsAsync<TObject>()

每个查询都有返回的记录数限制-因此,要获取该月的所有记录-我必须按ID排序并循环。这是当前“同步”调用的一些示例代码。只要得到结果,我就会检查更多。

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

虽然我了解到这些方法在内部仍在同步运行-我希望每次对ListTimeEntriesListServiceTicketsListProjectTickets的调用都是异步发生的,以加快速度。

我想我的想法/希望-由于缺乏知识-将是每个同步调用将使用不同的线程-从而加快了整个过程。我离基地远吗?

1 个答案:

答案 0 :(得分:0)

我会做类似以下的事情。您将需要了解有关API限制的潜在问题,并且当然有可能在所有4个线程完成之前至少有3个额外的请求正在进行中-如果要检索的页面很多,则要付出很小的代价。 / p>

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