Home > OS >  A second operation cannot be started when using ContinueWith
A second operation cannot be started when using ContinueWith

Time:07-12

I have a loop and within the loop I'm doing:

await Task.Delay(1000, ct).ContinueWith(async _ =>
{
    await SecondMethodAsync(ct);
});

The second method gets an entity using EF, sets some properties and saves the entity back to the datastore by calling await SaveChangesAsync() on the DbContext.

The above should wait for 1s and then continue with the second method. With the above implementation I get the following exception:

A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.

When I change the above to:

await Task.Delay(1000, ct);
await SecondMethodAsync(ct);

Everything works fine.

What is the difference with the above 2 snippets and how do I get to make the first snippet work?

CodePudding user response:

I think the reason is the following. Please correct me anybody if I am wrong.

The async lambda inside ContinueWith returns Task, which means the ContinueWith is actually done right after it gets that Task (not when the Task is completed). The program flow goes right into the next loop iteration and hitting that DBContext again.

So the following might also work since the return type of ContinueWith is Task<Task>:

await await Task.Delay(1000, ct).ContinueWith(async _ => {
    await SecondMethodAsync(ct); 
});

CodePudding user response:

From A Tour of Task, Part 7: Continuations by the async/await guru Stephen Cleary:

In conclusion, I do not recommend using ContinueWith at all, unless you are doing dynamic task parallelism (which is extremely rare). In modern code, you should almost always use await instead of ContinueWith. There are several benefits to await.

You know the solution and it's great:

await Task.Delay(TimeSpan.FromSeconds(1), ct);
await SecondMethodAsync(ct);

CodePudding user response:

I think your problem is because of Entity framework, as u mentioned in SecondMethodAsync you are using EF to do some stuff when you use ContinueWith the inner method will continue in another thread parallel to main, and the caller thread wont block and wait for it. but when you changed it to:

await Task.Delay(1000, ct);
await SecondMethodAsync(ct);

the main thread actually waits for the second operation as well. so if you use the EF context elsewhere in the code after ContinueWith, there might be concurrent calls to DB(in SecondMethodAsync and your main code simultaneously).

i suggest that you need to get a new instance of your dbContext inside the await SecondMethodAsync, or make sure that every call to db context must end before another call.

---- edit----

Michalor answer is simpler and better

  • Related