Слишком много задач приводит к превышению времени ожидания базы данных SQL - PullRequest
3 голосов
/ 29 марта 2011

Моя проблема в том, что я, очевидно, использую слишком много задач (потоков?), Которые вызывают метод, который запрашивает базу данных SQL Server 2008. Вот код:

for(int i = 0; i < 100000 ; i++)
{  
  Task.Factory.StartNew(() => MethodThatQueriesDataBase()).ContinueWith(t=>OtherMethod(t));  
}

Через некоторое время я получаю исключение времени ожидания SQL. Я хочу, чтобы фактическое количество потоков было низким (er), чем 100000, в буфер, скажем, "не более 10 за один раз". Я знаю, что могу управлять своими собственными потоками, используя ThreadPool, но я хочу использовать красоту TPL с ContinueWith .

Я посмотрел на Task.Factory.Scheduler.MaximumConcurrencyLevel, но у него нет сеттера.

Как мне это сделать?

Заранее спасибо!

ОБНОВЛЕНИЕ 1
Я только что протестировал класс LimitedConcurrencyLevelTaskScheduler (указанный Skeet) и продолжаю делать то же самое (время ожидания SQL).
Кстати, эта база данных получает более 800000 событий в день и никогда не имела сбоев или тайм-аутов от них. Звучит странно, что так и будет.

Ответы [ 3 ]

6 голосов
/ 29 марта 2011

Вы можете создать TaskScheduler с ограниченной степенью параллелизма , как описано здесь , затем создать TaskFactory и использоватьэта фабрика для запуска задач вместо Task.Factory.

3 голосов
/ 29 марта 2011

Задачи не являются 1: 1 с потоками - задачам назначаются потоки для выполнения из пула потоков, и пул потоков обычно поддерживается довольно небольшим (количество потоков == количество ядер ЦП), если задача / поток заблокирован в ожидании длительного синхронного результата - например, синхронного сетевого вызова или файлового ввода-вывода.

Таким образом, выполнение 10 000 задач не должно привести к получению 10 000 реальных потоков. Однако, если каждая из этих задач немедленно переходит в блокирующий вызов, вы можете получить больше потоков, но их все равно не должно быть 10000.

Что может происходить здесь, так это то, что вы перегружаете базу данных SQL слишком большим количеством запросов одновременно. Даже если система настраивает только несколько потоков для тысяч ваших задач, несколько потоков все равно может вызвать сбой, если назначение вызова однопоточное. Если каждая задача выполняет вызов в базу данных SQL, а интерфейс базы данных SQL или сама база данных координирует многопоточные запросы через блокировку одного потока, то все одновременные вызовы будут накапливаться в ожидании, пока блокировка потока не попадет в базу данных SQL для выполнения. , Нет гарантии того, какие потоки будут освобождены для последующего вызова в базу данных SQL, поэтому вы можете легко получить один «неудачный» поток, который начинает ожидать доступа к базе данных SQL раньше, но не входит в вызов базы данных SQL. до истечения времени ожидания блокировки.

Также возможно, что серверная часть SQL является многопоточной, но ограничивает количество одновременных операций из-за уровня лицензирования. То есть демонстрационный движок SQL допускает только 2 одновременных запроса, но полностью лицензированный движок поддерживает десятки одновременных запросов.

В любом случае вам нужно что-то сделать, чтобы снизить уровень параллелизма до более приемлемых уровней. Предложение Джона Скита об использовании TaskScheduler для ограничения параллелизма звучит как хорошее место для начала.

0 голосов
/ 29 марта 2011

Я подозреваю, что с обработкой соединений с БД что-то не так. Веб-серверы могут иметь тысячи одновременных запросов страниц на всех этапах SQL-активности. Держу пари, что попытки уменьшить количество одновременных задач действительно маскируют другую проблему.

Можете ли вы профилировать соединения SQL? Проверьте perfmon, чтобы увидеть, сколько активных соединений есть. Посмотрите, сможете ли вы как можно быстрее захватить и разблокировать соединения.

...