Leonardo Leonardo -4 years ago 91
C# Question

Async/Await - Await not holding as expected

When performing a long running operation I noticed that i could kickstart a long running sub-operation right off the start line and do other stuff while it fetches results from caches/databases.

The given operation is:

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 is a method used all over with the following signature:

public async Task<List<Agendas>> GetAgendas()


because it's widely used, i believe the problem is not there. As for
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);
}


EditNoReconnect
and
Save
are both sync methods.

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
is also a widely used method, so I think it's safe... it's signature is
private Task<AgendasCacheLookupResult> TryGetCache(


So, resuming the issue at hand: For a small set of items do in the sync session of the
Finalize
method the command
transaction.Complete()
is execute before
Save()
(inside BulkEditAgendas). For a regular or large amount of items, it works as expected.

This means that i'm not chaining the
Tasks
correctly, or my understanding of how Async/Await + Tasks/ContinueWith works is fundamentally incorrect. Where am I wrong?

Answer Source

The problem is most likely here:

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

First, what you return from this is the continuation task (result of ContinueWith). But body of ContinueWith ends when you do

return ags.BulkEditAgenda(currentAgendas);

So body of continuation ends potentially before BulkEditAgenda task is completed (you don't wait in any way for completion of BulkEditAgenda). So this line

await agendasTransitionTask;

Returns while BulkEditAgenda is still in progress. To clarify even more, note that what is returned from ExecuteAgendas is Task<Task> and result of await agendasTransitionTask is Task which represents your running BulkEditAgenda.

To fix, just use async\await like you do everywhere else:

private async Task ExecuteAgendas() {
   var currentAgengas = await ags.GetAgendas();            
   foreach (var item in currentAgendas) {    
       // do stuff            
   }
   await ags.BulkEditAgenda(currentAgendas);
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download