Calling Task.wait may not wait if the task has not

2019-02-18 23:08发布

问题:

I was reading Jeffrey Richter's clr via c# book and felt uncomfortable reading that task wait may not always wait and I quote

"When a thread calls the Wait method, the system checks if the Task that the thread is waiting for has started executing. If it has, then the thread calling Wait will block until the Task has completed running. But if the Task has not started executing yet, then the system may (depending on the TaskScheduler ) execute the Task by using the thread that called Wait . If this happens, then the thread calling Wait does not block; it executes the Task and returns immediately."

Can some one please share more insight and in which case can such a scenario may happen?

回答1:

I think this is unfortunately phrased. It's not that the Wait call returns before the task has finished executing; it's that the thread calling Wait may end up executing the task itself, rather than just blocking idly.

Sample code:

using System;
using System.Threading;
using System.Threading.Tasks;

class Test
{
    static void Main()
    {
        // Make sure DemonstrateIssue is already called in a ThreadPool
        // thread...
        Task task = Task.Run((Action) DemonstrateIssue);
        task.Wait();
    }

    static void DemonstrateIssue()
    {
        Console.WriteLine("DemonstrateIssue thread: {0}",
                          Thread.CurrentThread.ManagedThreadId);
        Action action = () => Console.WriteLine("Inner task thread: {0}",
            Thread.CurrentThread.ManagedThreadId);
        Task task = new Task(action);
        // Calling Start will just schedule it... we may be able to Wait
        // before it actually executed
        task.Start();
        task.Wait();
    }
}

Output every time I've run it:

DemonstrateIssue thread: 3
Inner task thread: 3

This takes advantage of the fact that the thread pool doesn't spin up threads immediately on demand - it waits for a while to see if an existing thread will become available before starting another one. If you add Thread.Sleep(5000); before the call to task.Wait(), you'll see the two tasks end up on different threads.