1

In MS documentation I happened to see this sample code and this part struck me as odd, to have nested using Task expressions. I haven't seen this before, what specifically it is achieving? Given the Task objects run in parallel how does this make sense?

using (BlockingCollection<int> bc = new BlockingCollection<int>())
        {
            // Spin up a Task to populate the BlockingCollection
            using (Task t1 = Task.Run(() =>
            {
                bc.Add(1);
                bc.Add(2);
                bc.Add(3);
                bc.CompleteAdding();
            }))
            {
                // Spin up a Task to consume the BlockingCollection
                using (Task t2 = Task.Run(() =>
                {
                    try
                    {
                        // Consume consume the BlockingCollection
                        while (true) Console.WriteLine(bc.Take());
                    }
                    catch (InvalidOperationException)
                    {
                        // An InvalidOperationException means that Take() was called on a completed collection
                        Console.WriteLine("That's All!");
                    }
                }))
                {
                    await Task.WhenAll(t1, t2);
                }
            }
        }
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589

1 Answers1

0

Task implements IDisposable, so this code will dispose both tasks as soon as they will finish(and not before due to await Task.WhenAll(t1, t2); line). As for when (you don't 99% of the time) and why should you dispose Task's there is nice article by Stephen Toub (and this answer on SO).

In the article there is next quote:

In .NET 4, the WaitHandle was lazily-initialized in a few situations: if the Task’s ((IAsyncResult)task).AsyncWaitHandle explicitly-implemented interface property was accessed, or if the Task was used as part of a Task.WaitAll or Task.WaitAny call and the Task was not yet completed. This made the answer to the “should I dispose” question slightly difficult, as if you had a lot of tasks being used with WaitAll/WaitAny, it might actually have been good to dispose of those tasks.

And BlockingCollection is available (according to docs) since 4.0 (before async/await), so I would assume that Task.WaitAll was used previously in the docs and when docs were rewritten to await Task.WhenAny those using's were forgotten and/or left despite not being needed anymore.

Submitted PR with proposal to change the sample in the API docs, will see if I'm correct =).

UPD

PR was accepted and merged.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132