Многопоточный сервис, BackgroundWorker vs ThreadPool? - PullRequest
24 голосов
/ 08 июля 2011

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

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

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

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

Ответы [ 5 ]

34 голосов
/ 08 июля 2011

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

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

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

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

10 голосов
/ 08 июля 2011

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

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

9 голосов
/ 08 июля 2011

Я написал довольно исчерпывающий обзор различных реализаций асинхронных фоновых задач в своем блоге .Резюме: предпочитаю 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).

3 голосов
/ 13 июля 2011

Определенно не 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

2 голосов
/ 08 июля 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...