Добрый день!
Есть программа, которая запускает несколько потоков одновременно для выполнения длинных задач. Потоки создаются "вручную" через new Thread ( ). Меня беспокоит возможность того, что в один прекрасный момент их станет слишком много, и система загнется. Соответственно, я решил вместо ручного создания потоков использовать пул потоков, чтобы оградить себя от создания такого количества потоков, которое система не вынесет. Решение спорное, но для его проверки я написал простой код:
for ( int i = 0; i < 10000; i++ )
System.Threading.ThreadPool.QueueUserWorkItem (
delegate ( object a ) { Console.WriteLine ( a ); System.Threading.Thread.Sleep ( 500 ); }, i );
Я ожидал, что увижу на экране сразу столько строк, каков размер пула (потому что они все, по моему мнению, запустятся сразу), после чего строки начнут помаленьку добавляться (по мере завершения задач и освобождения пула). На практике это оказалось совсем не так — строки появляются по чуть-чуть. Если время ожидания увеличить, например, до 5000, то задачи будут выполняться с задержкой примерно в секунду, и это время от Sleep не зависит.
Вопросы:
1) Как работает пул потоков, что результат такой неожиданный?
2) Имеет ли смысл использовать пул потоков для того, чтобы не нарваться на переполнение системных ресурсов? Если нет, то как лучше поступить? Задача достаточно простая: есть метод, вызываемый веб-сервером. Внутри него надо параллельно запустить еще 10 методов, дождаться их завершения (но ждать не больше 10 секунд), после чего немного обработать результаты (паралеллизм тут уже не требуется) и выйти.
Re: ThreadPool.QueueUserWorkItem, выполнение потоков
СА>2) Задача достаточно простая: есть метод, вызываемый веб-сервером. Внутри него надо параллельно запустить еще 10 методов, дождаться их завершения (но ждать не больше 10 секунд), после чего немного обработать результаты (паралеллизм тут уже не требуется) и выйти.
private int P1(){return 1;}
private bool P2(){return true;}
private void DoAll()
{
Task<int> task1 = Task.Factory.StartNew<int>(P1);
Task<bool> task2 = Task.Factory.StartNew<bool>(P2);
if (!Task.WaitAll(new Task[] { task1, task2 }, TimeSpan.FromSeconds(10)))
throw new Exception("Timeout");
int result1 = task1.Result;
bool result2 = task2.Result;
}
Re: ThreadPool.QueueUserWorkItem, выполнение потоков
От:
Аноним
Дата:
26.01.12 14:20
Оценка:
Здравствуйте, Степанов Андрей, Вы писали:
СА>1) Как работает пул потоков, что результат такой неожиданный?
По умолчанию размер пула соотвветствует кол-ву ядер и будет в процессе выполнения расти если требуется. Стартовый размер можно увеличить с помощью ThreadPool.SetMinThreads.
СА>2) Имеет ли смысл использовать пул потоков для того, чтобы не нарваться на переполнение системных ресурсов?
Имеет. Если целевой фреймворк .Net 4, то лучше пользоваться Task Parallel Library, которая упрощает организацию подобных задач.
Re: ThreadPool.QueueUserWorkItem, выполнение потоков
Здравствуйте, Степанов Андрей, Вы писали:
СА>2) Имеет ли смысл использовать пул потоков для того, чтобы не нарваться на переполнение системных ресурсов? Если нет, то как лучше поступить? Задача достаточно простая: есть метод, вызываемый веб-сервером. Внутри него надо параллельно запустить еще 10 методов, дождаться их завершения (но ждать не больше 10 секунд), после чего немного обработать результаты (паралеллизм тут уже не требуется) и выйти.
Да, имеет, но помещать в пул work items, в которых будут выполняться синхронные вызовы к веб-сервисам не нужно — нужно вызывать методы веб-сервиса асинхронно. Если есть возможность, использовать TPL.
Re[2]: ThreadPool.QueueUserWorkItem, выполнение потоков
Здравствуйте, -VaS-, Вы писали:
СА>>2) Задача достаточно простая: есть метод, вызываемый веб-сервером. Внутри него надо параллельно запустить еще 10 методов, дождаться их завершения (но ждать не больше 10 секунд), после чего немного обработать результаты (паралеллизм тут уже не требуется) и выйти.
VS>
Да, это примерно то, что надо, за исключением одного — я хочу убить те потоки (задачи, в данном случае), которые не успели отработать. Как это сделать?
Re[2]: ThreadPool.QueueUserWorkItem, выполнение потоков
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Степанов Андрей, Вы писали:
СА>>1) Как работает пул потоков, что результат такой неожиданный? А>По умолчанию размер пула соотвветствует кол-ву ядер и будет в процессе выполнения расти если требуется. Стартовый размер можно увеличить с помощью ThreadPool.SetMinThreads.
Спасибо за ответ! Так оно и есть, я пропустил этот метод при просмотре документации. Меня сгубило собственное предположение о том, что потоков сразу будет много
СА>>2) Имеет ли смысл использовать пул потоков для того, чтобы не нарваться на переполнение системных ресурсов? А>Имеет. Если целевой фреймворк .Net 4, то лучше пользоваться Task Parallel Library, которая упрощает организацию подобных задач.
В данном случае код занял всего 4 строки:
System.Threading.ThreadPool.SetMinThreads ( 100, 2 );
for ( int i = 0; i < 10000; i++ )
System.Threading.ThreadPool.QueueUserWorkItem (
delegate ( object a ) { Console.WriteLine ( a ); System.Threading.Thread.Sleep ( 5000 ); }, i );
Правда, он не содержит нужного мне таймаута, и это большой минус.
Как этот же код будет выглядеть в TPL? Как он будет выглядеть, если добавить таймаут каждому потоку?
Re[3]: ThreadPool.QueueUserWorkItem, выполнение потоков
От:
Аноним
Дата:
27.01.12 14:04
Оценка:
Здравствуйте, Степанов Андрей, Вы писали:
СА>Как этот же код будет выглядеть в TPL? Как он будет выглядеть, если добавить таймаут каждому потоку?
Выше привели пример кода. По отмене задач — Task Cancellation — т.е. после таймаута можно дёрнуть Cancel у CancellationTokenSource. Если возможность отмены во время выполнения задачи не критична, то флаг в теле задачи можно не проверять.