I have a method which loads some rows from a database, then performs an operation on every row.
Synchronously, it would look like this:
void DoSomething()
{
using( IWebService web = new WebService( ... ) )
using( IDatabaseService db = new DatabaseService( ... ) )
{
List<Entity> rows = db.GetRows( ... );
foreach( Entity row in rows )
{
RelatedInfo info = web.GetRelatedInfo( row.Foo );
web.MakeAnotherServiceCall( row.Bar );
db.UpdateEntity( row.Id, info );
}
}
}
My async
version looks like this (ConfigureAwait
omitted for brevity):
async Task DoSomething()
{
using( IWebService web = new WebService( ... ) )
using( IDatabaseService db = new DatabaseService( ... ) )
{
List<Entity> rows = await db.GetRows( ... );
List<Task> tasks = new List<Task>();
foreach( Entity row in rows )
{
Entity localRow = row; // new alias to prevent capturing the foreach object in the closure
Task task = new Task( async() =>
{
RelatedInfo info = await web.GetRelatedInfo( localRow.Foo );
await web.MakeAnotherServiceCall( localRow.Bar );
await db.UpdateEntity( localRow.Id, info );
} );
tasks.Add( task );
}
await Task.WhenAll( tasks );
}
}
(For unrelated reasons I'm unable to test this right now, but I should be able to test it in a few days).
Notice how my per-row operation creates a new Task
which is eventually awaited
(inside Task.WhenAll
).
Remember that I'm not wanting to start any background/pool threads (if I wanted that I would just use Task.Run
). Instead I'm wanting all of these per-row work-items to run asynchronously on the same thread... but in my example above, do they?
The examples I've seen on StackOverflow so far, such as Asynchronous foreach - use the same pattern as me, except they use a separate class-level method (or they cop-out using Task.Run
) - they don't use a lambda inside an explicit Task
constructor:
async Task DoSomething()
{
using( IWebService web = new WebService( ... ) )
using( IDatabaseService db = new DatabaseService( ... ) )
{
List<Entity> rows = await db.GetRows( ... );
List<Task> tasks = new List<Task>();
foreach( Entity row in rows )
{
Task t = DoRow( row, web, db );
tasks.Add( t );
}
Task.WhenAll( tasks );
}
}
private static async Task DoRow(Entity row, IWebService web, IDatabaseService db)
{
RelatedInfo info = await web.GetRelatedInfo( localRow.Foo );
await web.MakeAnotherServiceCall( localRow.Bar );
await db.UpdateEntity( localRow.Id, info );
}
My understanding so-far of Task
and lambdas is that my lambda-based code should have the same asynchronous semantics as the separate-method code above - but I'm uncertain.
Update:
Perhaps a better way of phrasing my question is using continuations, which await
abstracts away:
I understand this is what I'm wanting to achieve, but using the await
keyword so I don't have to use ContinueWith
manually, and without using Task.Run
:
List<Task> tasks = new List<Task>();
foreach( Entity row in rows )
{
Entity localRow = row;
Task t = web
.GetRelatedInfo( localRow.Foo )
.ContinueWith( t1 => new { RelatedInfo = t1.Result, WebCall2 = web.MakeAnotherServiceCall( localRow.Bar ) } )
.ContinueWith( t2 => db.UpdateEntity( localRow.Id, t2.RelatedInfo ) );
tasks.Add( t );
}
Task.WhenAll( tasks );