I've hit a problem trying to process multiple Tasks in parallel (or however the runtime feels fit) when using disposable objects. In the following code snippet, each Processor object is immediately disposed BEFORE it has done the required work.
async public Task ProcessData(IEnumerable<int> data)
{
var tasks = new List<Task>();
foreach (var d in data)
{
using (var processor = new Processor(d))
{
processor.Completed += (sender, e) => { // Do something else };
tasks.Add(processor.ProcessAsync());
}
}
await Task.WhenAll(tasks);
}
Re-writing the code as below results in the each Processor performing its processing and THEN being disposed, but it's not the most efficient way to run multiple Tasks that don't depend on one another.
async public Task ProcessData(IEnumerable<int> data)
{
foreach (var d in data)
{
using (var processor = new Processor(d))
{
processor.Completed += (sender, e) => { // Do something else };
await processor.ProcessAsync();
}
}
}
Can someone please explain why the first example is disposing 'early' and give an example of the best code pattern for this situation.
It helps to think of await
as pausing the current method, even though it doesn't block the thread.
In your first example, when you are executing the foreach
loop, each time you create a Processor
, start an operation (saving the operation's Task
in a list), and then dispose the Processor
. After your foreach
loop is finished, then you (asynchronously) wait for all the operations to complete.
In your second example, when you are executing the foreach
loop, each time you create a Processor
, start an operation, (asynchronously) wait for it to complete, and then dispose the Processor
.
To fix this, you should write a helper method, as such:
private static async Task ProcessData(int data)
{
using (var processor = new Processor(d))
{
processor.Completed += (sender, e) => { /* Do something else */ };
await processor.ProcessAsync();
}
}
Your helper method defines a higher-level operation, which will take care of disposing its own Processor
resource at the appropriate time. Then you can start all your work concurrently as such:
public async Task ProcessData(IEnumerable<int> data)
{
...
await Task.WhenAll(data.Select(d => ProcessData(d)));
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With