10

Here is a console program want 10 threads start in batch, wait 5 seconds, and stop in batch.

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Action<int> act = (i) =>
        {
            Console.Write("start {0} ", i);
            Thread.Sleep(5000);
        };

        act.BeginInvoke(index, OnTaskComplete, index);
    });

    Console.ReadKey();
}

static void OnTaskComplete(IAsyncResult res)
{
    Console.Write("finish {0} ", res.AsyncState);
}

but the result is not what I expected, 10 threads start one-by-one SLOWLY(around 1 second interval), even some "finish" comes out before some "start".

when comment out Thread.Sleep, all threads start and finish in flash.

Does Thread.Sleep affect other threads? Is there anyway to make a pure idle time?

/-----------------------------edit-----------------------------

same problem also happen in:

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Console.Write("start {0} ", index);
        Thread.Sleep(5000);
        Console.Write("fnish {0} ", index);
    });

    Console.ReadKey();
}

----------------------Edit------------------------

finally I found a lovely way to substitute thread.sleep

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Console.Write("start {0} ", index);

        var t1 = new System.Threading.Timer(new TimerCallback(MyTimerCallback), index, 5000, 0); 
    });

    Console.ReadKey();
}

static void MyTimerCallback(object o)
{
    Console.Write("Timer callbacked ");
}
davmos
  • 9,324
  • 4
  • 40
  • 43
demaxSH
  • 1,743
  • 2
  • 20
  • 27
  • Can you define pure idle time. Thread.Sleep only guarantees that the thread will sleep at least for the amount specified. If ThreadA and ThreadB go to sleep for the same time , it is not guaranteed that they will wake up in the reverse order. – parapura rajkumar Jun 01 '11 at 02:47
  • 1
    Just curious -- why have the `act` executed with `BeginInvoke`? Why not just put the code from the action directly in the first callback, with a call to `OnTaskComplete` at the end (since the callback is already asynchronous on another thread, if I understand correctly)? – Cameron Jun 01 '11 at 02:55
  • 1
    @raj. what I am wondering is why these thread starts one-by-one SLOWLY(it costed around 10 seconds to start these threads). I suppose they should begin in batch. – demaxSH Jun 01 '11 at 02:55

3 Answers3

13

This is by design. You are seeing the threadpool manager trying to keep a limited number of threads in the executing state. Important to ensure that your program isn't running more threads than your machine has cpu cores. That's inefficient, less work gets done when Windows is forced to start swapping the cores between active threads. The threadpool manager isn't smart enough to know that the thread is sleeping and not actually performing any work.

On a dual-core machine, you'll see the first 2 threads starting right away. Then additional threads are allowed to run, one by one with a one second interval when the thread manager notices that the active threads are not making any progress and are probably blocked. The order in which threads are released and execute the Console.Write() call is not deterministic.

This is an artificial test of course, real threads don't sleep. If you have threads that block for a long time, waiting for an I/O request to complete for example then using threadpool threads (tasks) is not the best solution.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I suppose if the number of cpu-core is less than the threads, threads will be proceeded by time slice. I did found first 2 thread starting simultaneously, but the 3rd, 4rd threads flowed on after 1 or 2 sec, which seems to long. maybe the thread.sleep cannot be well time sliced. – demaxSH Jun 01 '11 at 03:24
  • No, you missed the essential part of the answer. It is the threadpool manager that prevents a thread from starting when the cores are busy. – Hans Passant Jun 01 '11 at 03:28
  • 1
    Actually I am trying to spoof an I/O request waiting scenario when making unit-test. and I cannot find a better way than using thread.sleep. – demaxSH Jun 01 '11 at 03:35
  • This is an ok simulation of the threadpool dispatch queue backed up with threads that are blocked on something. The threadpool manager will not spin up more threads than the minimum configured threads until it's clear that the queued delegates are not going to get serviced fast enough. – Jake T. Jun 01 '11 at 04:21
6

TaskCreationOptions.LongRunning will 'remove' the ThreadPool limitation.
I don't know the easy way to specify TaskCreationOptions.LongRunning for Parallel.For.
However, you can achieve the same effect using Task class:

Action<int> action = i =>
    {
        Console.Write("start {0} ", i);
        Thread.Sleep(5000);
        Console.Write("finish {0} ", i);
    };

var tasks = Enumerable.Range(0, 100)
    .Select(arg => Task.Factory.StartNew(() => action(arg), TaskCreationOptions.LongRunning))
    .ToArray();

Task.WaitAll(tasks);

Without TaskCreationOptions.LongRunning it will run exactly the same way as your Parallel.For did.

Alex Aza
  • 76,499
  • 26
  • 155
  • 134
3

I updated the code slightly to show the ThreadID when writing to the Console:

Console.WriteLine("start index:{0} thread id:{1} Time:{2} ", index, Thread.CurrentThread.ManagedThreadId.ToString(), DateTime.Now.ToLongTimeString());
Thread.Sleep(5000);
ConsoleWriteLine("finish index:{0} thread id:{1} Time:{2} ", index, Thread.CurrentThread.ManagedThreadId.ToString(), DateTime.Now.ToLongTimeString());

My machine is a dual core, here's the output I get. This should give you a sense of what's happening. Remember, the loop may not always run in order, i.e.. 0 to 9, being parallel, it grabs a chunk of your array and runs each item through the lambda.

Output:

start  index:1 thread id:11 Time:11:07:17 PM
start  index:0 thread id:9  Time:11:07:17 PM
start  index:5 thread id:10 Time:11:07:17 PM
start  index:6 thread id:12 Time:11:07:18 PM
start  index:2 thread id:13 Time:11:07:19 PM
start  index:7 thread id:14 Time:11:07:20 PM
start  index:3 thread id:15 Time:11:07:21 PM
start  index:8 thread id:16 Time:11:07:22 PM
finish index:0 thread id:9  Time:11:07:22 PM
start  index:4 thread id:9  Time:11:07:22 PM
finish index:1 thread id:11 Time:11:07:22 PM
start  index:9 thread id:11 Time:11:07:22 PM
finish index:5 thread id:10 Time:11:07:22 PM
finish index:6 thread id:12 Time:11:07:23 PM
finish index:2 thread id:13 Time:11:07:24 PM
finish index:7 thread id:14 Time:11:07:25 PM
finish index:3 thread id:15 Time:11:07:26 PM
finish index:8 thread id:16 Time:11:07:27 PM
finish index:4 thread id:9  Time:11:07:27 PM
finish index:9 thread id:11 Time:11:07:27 PM
534F
  • 57
  • 6
Justin Largey
  • 1,934
  • 1
  • 16
  • 21
  • well the order doesn't matter. but we can see some thread starts after 5 seconds!. it is not parallel starting. I think the latest one start after 100millisecond is acceptable. – demaxSH Jun 01 '11 at 03:22