异步/等待 - 等待没有按预期保持

时间:2017-03-22 19:43:56

标签: c# .net asynchronous async-await

当执行长时间运行的操作时,我注意到我可以在起始行开始执行长时间运行的子操作,并在从缓存/数据库中获取结果时执行其他操作。

给定的操作是:

public async Task<Fichaclis> Finalize()
{
     using (TransactionScope transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
     {
         transactionTimer.Start();
         var agendasTransitionTask = ExecuteAgendas();

                ... DO ALOT OF SYNC OPERATIONS ...
         await agendasTransitionTask;
         transaction.Complete();
     }
}
private Task ExecuteAgendas()
{
    return ags.GetAgendas().ContinueWith((prev) =>
    {
        var currentAgendas = prev.Result;
        foreach (var item in currentAgendas)
        {
            ... DO QUICK SYNC STUFF...
        }
        return ags.BulkEditAgenda(currentAgendas);
    });
}

GetAgendas是一种全部使用的方法,具有以下签名:
public async Task<List<Agendas>> GetAgendas()
因为它被广泛使用,我相信问题不存在。至于BulkEditAgenda

public async Task BulkEditAgenda(IEnumerable<Agendas> agendas)
{
    if (agendas == null || agendas.Count() == 0)
    {
        return;
    }
    var t1 = AddOrUpdateCache(agendas);
    var t2 = Task.Factory.StartNew(() =>
    {
        try
        {
            foreach (var item in agendas)
            {
                EditNoReconnection(item);
            }
            Save();
        }
        catch (Exception ex)
        {
            //log
            throw;
        }
    });
    await Task.WhenAll(t1, t2);
}

EditNoReconnectSave都是同步方法。

private Task AddOrUpdateCache(IEnumerable<Agendas> agendas)
{
    var tasks = new List<Task>();
    foreach (var item in agendas)
    {
        tasks.Add(TryGetCache(item)
            .ContinueWith((taskResult) =>
            {
                ...DO QUICK SYNC STUFF...
            })
        );
    }
    return Task.WhenAll(tasks);
}

TryGetCache也是一种广泛使用的方法,所以我觉得它很安全......它的签名是private Task<AgendasCacheLookupResult> TryGetCache(

因此,恢复手头的问题:对于Finalize方法的同步会话中的一小组项,命令transaction.Complete()Save()之前执行(在BulkEditAgendas内)。对于常规或大量的项目,它按预期工作 这意味着我没有正确地链接Tasks,或者我对Async / Await + Tasks / ContinueWith如何工作的理解从根本上说是不正确的。我哪里错了?

1 个答案:

答案 0 :(得分:6)

问题很可能在这里:

private Task ExecuteAgendas()
{
    return ags.GetAgendas().ContinueWith((prev) =>
    {
        var currentAgendas = prev.Result;
        foreach (var item in currentAgendas)
        {
            ... DO QUICK SYNC STUFF...
        }
        return ags.BulkEditAgenda(currentAgendas);
    });
}

首先,你从中返回的是继续任务(ContinueWith的结果)。但是ContinueWith的主体在你做

时结束
return ags.BulkEditAgenda(currentAgendas);

因此,在 BulkEditAgenda任务完成之前,延续体可能结束(您不会以任何方式等待完成BulkEditAgenda)。所以这一行

await agendasTransitionTask;

BulkEditAgenda仍在进行中时返回。为了进一步说明,请注意ExecuteAgendas返回的内容为Task<Task>await agendasTransitionTask的结果为Task,代表您正在投放BulkEditAgenda

要修复,只需像在其他地方一样使用async \ await:

private async Task ExecuteAgendas() {
   var currentAgengas = await ags.GetAgendas();            
   foreach (var item in currentAgendas) {    
       // do stuff            
   }
   await ags.BulkEditAgenda(currentAgendas);
}