Препятствует ли Thread.Sleep другим потокам? - PullRequest
9 голосов
/ 01 июня 2011

Вот консольная программа, которая хочет, чтобы 10 потоков запускались в пакетном режиме, ждали 5 секунд и останавливались в пакетном режиме.

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);
}

, но результат не тот, который я ожидал, 10 потоков запускаются один за другимМЕДЛЕННО (с интервалом около 1 секунды), даже некоторое «окончание» появляется перед некоторым «началом».

когда закомментирован Thread.Sleep, все потоки начинаются и заканчиваются во флэш-памяти.

Делает Thread.Sleep влияет на другие темы?Есть ли вообще сделать простой простой?

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

такая же проблема возникает и в:

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();
}

---------------------- Редактировать ------------------------

наконец-то я нашел прекрасный способ заменить нить.спи

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 ");
}

Ответы [ 3 ]

13 голосов
/ 01 июня 2011

Это по замыслу. Вы видите, как диспетчер потоков пула пытается сохранить ограниченное количество потоков в состоянии выполнения. Важно убедиться, что ваша программа не запускает больше потоков, чем ваша машина имеет ядра процессора. Это неэффективно, требуется меньше работы, когда Windows вынуждена менять местами ядра между активными потоками. Менеджер пула потоков недостаточно умен, чтобы знать, что поток спит и фактически не выполняет никакой работы.

На двухъядерной машине вы увидите, что первые 2 потока начинаются сразу же. Затем разрешается запуск дополнительных потоков, один за другим с интервалом в одну секунду, когда диспетчер потоков замечает, что активные потоки не выполняются и, вероятно, заблокированы. Порядок, в котором потоки освобождаются и выполняют вызов Console.Write (), не является детерминированным.

Это, конечно, искусственный тест, настоящие темы не спят. Если у вас есть потоки, которые блокируют в течение длительного времени, например, ожидая завершения запроса ввода-вывода, тогда использование потоков пула потоков (задач) - не лучшее решение.

5 голосов
/ 01 июня 2011

TaskCreationOptions.LongRunning будет «снимать» ограничение ThreadPool.
Я не знаю простой способ указать TaskCreationOptions.LongRunning для Parallel.For.
Однако вы можете добиться того же эффекта, используя класс Task:

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);

Без TaskCreationOptions.LongRunning он будет работать точно так же, как ваш Parallel.For.

3 голосов
/ 01 июня 2011

Я немного обновил код, чтобы показать ThreadID при записи в консоль:

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());

Моя машина двухъядерная, вот вывод, который я получаю. Это должно дать вам ощущение того, что происходит. Помните, что цикл может не всегда выполняться по порядку, т. Е. От 0 до 9, будучи параллельным, он захватывает часть вашего массива и запускает каждый элемент через лямбду.

Выход:

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
...