Многопоточный сервис, BackgroundWorker vs ThreadPool?

У меня есть.NET 3.5 Windows Service. Я тестирую с небольшим приложением, которое просто спит потоки после их запуска, для случайных временных интервалов от 300 до 6500 мс. У меня есть различные вопросы по этому вопросу.

  1. Является BackgroundWorker действительно предназначен для использования только в приложениях WinForms или это просто глупость, как именно он настроен на этот эффект?
  2. Я читал о ThreadPoolС этим вопросом и этим. Я не совсем уверен, насколько для меня проблема в том, что потоки будут длиться где-то от полсекунды до нескольких секунд. Достаточно ли этой причины, чтобы искать где-нибудь еще?
  3. Мне лучше всего самому создавать фоновые темы?

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

Мне бы очень хотелось, чтобы на этот вопрос ответили для.NET 3.5 Framework, но если есть лучшие или более эффективные способы сделать это в.NET 4.0, я бы тоже хотел прочитать о них. Ссылки с дополнительной информацией также приветствуются.

5 ответов

Решение

Значение в BackgroundWorker является то, что он может поднять его ProgressChanged а также RunworkerCompleted событие в потоке, который создал его экземпляр. Что делает его очень удобным в программах, которые не поддерживают бесплатную многопоточность.

Для того, чтобы это работало должным образом, требуется, чтобы SynchronizationContext.Current свойство ссылается на поставщика синхронизации не по умолчанию. Поставщик, который отвечает за маршалинг вызовов из одного потока в другой..NET Framework имеет двух поставщиков, которые легко доступны: System.Windows.Forms.WindowsFormsSynchronizationContext а также System.Windows.Threading.DispatcherSynchronizationContext, Эти провайдеры обрабатывают синхронизацию для Winforms и WPF соответственно.

Существует соединение, Winforms и WPF - обе библиотеки классов, в которых есть проблема с многопоточностью. Оба реализуют графические интерфейсы пользователя и основанные на Windows графические пользовательские интерфейсы, по сути, небезопасные. Окна Windows могут быть обновлены только из потока, который их создал. Другими словами, эти пользовательские поставщики синхронизации существуют, потому что они крайне необходимы. Также следует отметить, что они работают, используя преимущества работы потоков пользовательского интерфейса. Поток пользовательского интерфейса выполняет код, управляемый событиями, прокачивая цикл сообщений для получения уведомлений. С помощью этого механизма поставщик синхронизации может внедрять вызовы в обработчики событий. Это не случайно.

Возвращаясь к теме, служба Windows не имеет такой возможности. Он не имеет графического интерфейса и не устанавливает пользовательский поставщик синхронизации. В качестве таких, BackgroundWorker не предоставляет функции, полезной в сервисе. Без пользовательского поставщика синхронизации поставщик по умолчанию просто запускает события в потоке потоков. Что бесполезно, вы также можете запустить событие из вашего рабочего потока. Получить события для запуска в другом конкретном потоке очень трудно, если только вы не воссоздаете механизм цикла сообщений или не подключаетесь к соединению Winforms и не создаете смоделированный поток пользовательского интерфейса, используя невидимое окно. Что не является чем-то необычным, кстати.

BackgroundWorker был разработан для упрощения взаимодействия задачи, работающей в фоновом потоке, с пользовательским интерфейсом. Вы увидите отличный ответ о том, когда использовать BackGroundWorker, ThreadPool и просто Thread в BackgroundWorker vs background Thread.

Я думаю, что это отвечает на вопрос:).

Я написал довольно полный обзор различных реализаций асинхронных фоновых задач в моем блоге. Резюме: предпочитаю Task; второй выбор будет BackgroundWorker; и только использовать Thread или же ThreadPool.QueueUserWorkItem если вам действительно нужно.

Причины: легче обнаружить и исправить ошибки, а также синхронизировать их обратно с пользовательским интерфейсом.

Чтобы ответить на ваши конкретные вопросы:

BackgroundWorker работает на любом хосте, включая WinForms и WPF (и даже ASP.NET!), потому что он основан наSynchronizationContext, Службы Windows не имеют SynchronizationContext, но вы можете использовать ActionThread из библиотеки Nito.Async, которая поставляется с SynchronizationContext,

Если я правильно прочитал ваш вопрос, у вас есть Threadс и рассматривают ThreadPool а также BackgroundWorker, Из этих вариантов я рекомендую BackgroundWorker, но если у вас есть шанс, используйте новый Task класс в.NET 4.0 (если вы устанавливаете Microsoft Rx, вы также можете использовать Task в.NET 3.5).

Определенно не Backgroundworker для службы

Вы должны перейти с Задачи в пространстве имен System.Threading.Tasks, также могут использовать задачи Параллельное выполнение потоков

http://msdn.microsoft.com/en-us/library/dd460717.aspx
Я цитирую: "Начиная с.NET Framework 4, TPL является предпочтительным способом написания многопоточного и параллельного кода".

Некоторые чтения:
http://msdn.microsoft.com/en-us/library/dd997413%28VS.100%29.aspx

Начни здесь:
http://msmvps.com/blogs/brunoboucard/archive/2010/04/09/parallel-programming-task-oriented-parallel-part-1.aspx
http://msmvps.com/blogs/brunoboucard/archive/2010/05/18/parallel-programming-in-c-4-0-task-oriented-parallel-programming-part-2.aspx
http://msmvps.com/blogs/brunoboucard/archive/2010/11/06/parallel-programming-with-c-4-0-part-3.aspx

Больше примеров
http://www.dotnetcurry.com/ShowArticle.aspx?ID=489
http://www.dotnetfunda.com/articles/article984-parallel-compting-in-csharp-40-.aspx

Я думаю, что ваш третий выбор лучше. Я сделал аналогичные вещи со службами Windows в.Net 3.5 и обнаружил, что создание собственных потоков - хороший способ, особенно с потоками, взаимодействующими с веб-службами.

Я создаю рабочий экземпляр и отправляю ему обратный вызов, который сигнализирует службе, когда она завершена. Я храню готовые темы в Queue и очистить их в соответствии с максимальным количеством одновременных потоков, которые я хочу. Если все, что вас волнует, это количество запущенных служб, вы можете отслеживать их с помощью простого счетчика. Я предпочитаю хранить каждый работающий рабочий экземпляр в Dictionary ключом потока ManagedThreadId так, чтобы я мог легко сигнализировать каждый экземпляр, если я хочу выключить его чисто. Это также удобно для опроса запущенных экземпляров для проверки статуса.

Другие вопросы по тегам