2015-10-01 2 views
3

У меня есть службы Windows, который имеет код, подобный следующему:Параллельное программирование для службы Windows

List<Buyer>() buyers = GetBuyers(); 
var results = new List<Result(); 

Parallel.Foreach(buyers, buyer => 
{ 
    // do some prep work, log some data, etc. 

    // call out to an external service that can take up to 15 seconds each to return 
    results.Add(Bid(buyer));  
} 

// Parallel foreach must have completed by the time this code executes 
foreach (var result in results) 
{ 
    // do some work 
} 

Это все хорошо и хорошо, и это работает, но я думаю, что мы страдает от вопроса масштабируемости. Мы составляем в среднем 20-30 входящих соединений в минуту, и каждый из этих подключений запускает этот код. Коллекция «покупателей» для каждого входящего соединения может иметь от 1 до 15 покупателей. Иногда наш входящий подсчет подключается к 100 + соединениям в минуту, и наш сервер останавливается.

Потребление процессора составляет всего около 50% на каждом сервере (два балансных 8-ядерных серверов с балансировкой нагрузки), но количество потоков продолжает расти (до 350 потоков в процессе), а время отклика для каждого входящего соединения - от 3 -4 секунды до 1,5-2 минут.

Я подозреваю, что приведенный выше код отвечает за наши проблемы с масштабируемостью. Учитывая этот сценарий использования (параллелизм для операций ввода-вывода) в службе Windows (без интерфейса), Parallel.ForEach лучший подход? У меня нет большого опыта работы с асинхронным программированием, и я с нетерпением жду возможности воспользоваться этой возможностью, чтобы узнать больше об этом, подумал я начал здесь, чтобы получить совет сообщества, чтобы дополнить то, что я смог найти в Google.

+0

В среднем, сколько запросов выполняется в любой момент? –

+3

Это не похоже на использование 'Parallel.For', поскольку большая часть потоков блокирует создание пула потоков для создания новых потоков. Вместо IO вместо IOS используйте async. –

+0

будет сокращение помощи MaxDegreeOfParallelism 'Parallel.ForEach (список, новые ParallelOptions() \t \t \t { \t \t \t \t MaxDegreeOfParallelism = Environment.ProcessorCount, \t \t \t}, parallelAction);' – ziddarth

ответ

3

Parallel.Foreach имеет ужасный недостаток в дизайне. Он подвержен потреблению всех доступных ресурсов пула потоков с течением времени. Количество потоков, которые он будет порождать, буквально неограниченно. Вы можете получить до 2 новых в секунду, управляемых эвристикой, которые никто не понимает. В CoreCLR встроен алгоритм альпинизма, который просто не работает.

вызова на внешний сервис

Вероятно, вы должны выяснить, что это право степени параллельности вызова этой услуги. Вам нужно узнать, проверив разные суммы.

Тогда вам нужно ограничить Parallel.Foreach, чтобы создать столько потоков, сколько хотите. Вы можете сделать это, используя фиксированный параллелизм TaskScheduler.

Или вы измените его, чтобы использовать async IO и используйте SemaphoreSlim.WaitAsync. Таким образом, нити не блокируются. Исчерпывание пула решается этим и перегрузкой внешней службы.

Смежные вопросы