2011-12-24 3 views
4

Ищу соответствующий шаблон и лучший современный способ решить следующую задачу:Обработка нескольких входов в фоновом режиме в приложении C# .NET4

Мое приложение ожидает входные сигналы от нескольких источников, например: графический интерфейс пользователя, мониторинг файловая система, голосовая команда, веб-запрос и т. д. Когда вход получен, мне нужно отправить его на какой-то метод ProcessInput(InputData arg), который начнет обработку данных в фоновом режиме, не блокируя приложение для получения и обработки большего количества данных, а в некоторых возвращайте некоторые результаты всякий раз, когда обработка завершена. В зависимости от ввода обработка может занять значительно меньшее время. Для начала мне не нужна возможность проверить ход или отменить обработку.

После прочтения десятка статей на MSDN и некоторых · блоги, · рок-звезда программистов Я действительно путают, что шаблон должен быть использован здесь, и что еще более важно, какие функции .NET

Мои выводы:

  1. ThreadPool.QueueUserWorkItem - проще понять, не очень удобно о возвращении результатов
  2. BackgroundWorker - кажется, используется только лишь для сравнительно простых задач, все рабочие работают на одном потоке?
  3. Event-based Asynchronous Pattern
  4. Tasks in Task Parallel Library
  5. C# 5 async/await - это, кажется, ярлыки для задач из Task Parallel

Примечания:

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

Это не веб-приложение.

Моя проблема напоминает мне TCP-сервер (на самом деле любой сервер), где приложение постоянно прослушивает новые подключения/данные на нескольких сокетах, я нашел статью Asynchronous Server Socket, и мне любопытно, может ли этот шаблон быть возможным решением для меня.

ответ

3

Мое приложение ожидает входные сигналы от нескольких источников, например: графический интерфейс пользователя, контроль файловую систему, голосовые команды, веб-запрос и т.д.

я сделал целый много асинхронного программирования в мое время. Мне полезно различать фоновые операции и асинхронные события. «Фоновая операция» - это то, что вы инициируете, и через некоторое время оно завершается. «Асинхронное событие» - это то, что всегда происходит независимо от вашей программы; вы можете подписаться, получить события на время, а затем отказаться от подписки.

Таким образом, входы графического интерфейса и мониторинг файловой системы являются примерами асинхронных событий; тогда как веб-запросы представляют собой фоновые операции. Фоновые операции также можно разделить на привязку к ЦП (например, обработку некоторого ввода в конвейере) и привязку ввода/вывода (например, веб-запрос).

Я делаю это различие особенно в .NET, потому что разные подходы имеют разные сильные и слабые стороны. При проведении оценок вы также должны учитывать, как ошибки распространяются.

Во-первых, варианты, которые Вы уже нашли:

  1. ThreadPool.QueueUserWorkItem - почти самый худший вариант вокруг. Он может обрабатывать только фоновые операции (без событий) и не обрабатывает операции с привязкой к I/O. Возвращаемые результаты и ошибки являются как ручными.
  2. BackgroundWorker (BGW) - не самое худшее, но определенно не лучшее. Он также обрабатывает только фоновые операции (без событий) и не обрабатывает операции с привязкой к I/O. Каждый BGW работает в своем собственном потоке - что плохо, потому что вы не можете воспользоваться преимуществами самозанятости работы пула потоков. Кроме того, уведомления о завершении (обычно) помещаются в очередь на один поток, что может вызвать узкое место в очень загруженных системах.
  3. Асинхронный шаблон на основе событий (EAP) - это первый вариант из вашего списка, который будет поддерживать асинхронные события, а также фоновые операции, а также может эффективно обрабатывать операции с привязкой к вводу/выводу. Тем не менее, некорректно запрограммировать программу, и она имеет ту же проблему, что и BGW, где уведомления о завершении (обычно) помещаются в один поток. (Обратите внимание, что BGW - это EAP, применяемый к фоновым операциям с привязкой к процессору). Я написал a library, чтобы помочь в написании компонентов EAP вместе с некоторыми сокетами на основе EAP. Но я не рекомендую этот подход; в наши дни есть лучшие варианты.
  4. Tasks в параллельной библиотеке задач - Task - лучший вариант для фоновых операций, как связанных с ЦП, так и привязки ввода-вывода. I review several background operation options on my blog - но это сообщение в блоге не затрагивает асинхронные события вообще.
  5. C# 5 async/await - Они позволяют более естественное выражение фоновых операций Task. Они также предлагают простой способ синхронизации с контекстом вызывающего, если вы хотите (полезно для операций, инициированных UI).

Из этих вариантов async/await является самым простым в использовании, с Task близкими вторым. Проблема заключается в том, что они были предназначены для фоновых операций, а не для асинхронных событий.

Любой источник асинхронного события может потребляться с использованием асинхронных операций (например, Task), если у вас есть достаточный буфер для этих событий. Когда у вас есть буфер, вы можете просто перезапустить асинхронную операцию каждый раз, когда она завершается. Некоторые буферы предоставляются ОС (например, сокеты имеют буферы чтения, окна пользовательского интерфейса имеют очереди сообщений и т. Д.), Но вам, возможно, придется предоставлять другие буферы.

Сказав, что, вот мои рекомендации:

  1. Task-based Asynchronous Pattern (TAP) - с использованием либо await/async или Task непосредственно, использование ТАР для моделирования, по меньшей мере фоновые операции.
  2. TPL Dataflow (часть VS Async) - позволяет вам создавать «трубопроводы» для передачи данных. Поток данных основан на Task с. Недостатком Dataflow является то, что он все еще развивается и (IMO) не так стабилен, как остальная поддержка Async.
  3. Reactive Extensions (Rx) - это единственный вариант, специально предназначенный для асинхронных событий, а не только для фоновых операций. Он официально выпущен (в отличие от VS Async и Dataflow), но кривая обучения более крутая.

Все три из этих вариантов являются эффективными (с использованием пула потоков для любой реальной обработки), и все они имеют четко определенную семантику для обработки ошибок и результатов. Я рекомендую использовать TAP как можно больше; эти части затем могут быть легко интегрированы в Dataflow или Rx.

Вы упомянули «голосовые команды» как один из возможных источников входного сигнала. Вас может заинтересовать BuildWindows video, где поёт Стивен Туб, и использует Dataflow для гармонизации своего голоса в почти реальном времени. (Стивен Туб - один из гениев за TPL, Dataflow и Async).

+0

Видеоролик BuildWindows, как правило, очень воспитывает. Благодаря! – m0s

2

IMO, использующий пул потоков, является способом перехода WRT на обработку ввода. Взгляните на http://smartthreadpool.codeplex.com. Он обеспечивает очень хороший API (с использованием дженериков) для ожидания результатов. Вы можете использовать это в сочетании с реализацией Asynchronous Socket Socket. Возможно также стоит взглянуть на Power Threading Джеффа Рихтера Lib: http://www.wintellect.com/Resources/Downloads

+0

Спасибо за указатели, я проверяю пул интеллектуальных потоков, кажется интересным и сложным ... такие задачи всегда сложны:/ – m0s

1

Я никоим образом не специалист по этим вопросам, но недавно я провел некоторое исследование по этому вопросу, и я очень доволен достигнутыми результатами с библиотекой MS TPL. Задачи дают вам хорошую обертку вокруг потоков ThreadPool и оптимизированы для параллельной обработки, поэтому они обеспечивают большую производительность. Если вы можете использовать .NET 4.0 для своего проекта, вам следует, вероятно, изучить задачи. Они представляют собой более продвинутый способ работы с асинхронными операциями и обеспечивают хороший способ отмены выполняемых операций с использованием объектов CancellationToken.

Вот короткий пример доступа к UI поток из различных нитей с помощью задач:

private void TaskUse() 
    { 
     var task = new Task<string>(() => 
      { 
       Thread.Sleep(5000); 
       return "5 seconds passed!"; 
      }); 
     task.ContinueWith((tResult) => 
      { 
       TestTextBox.Text = tResult.Result; 
      }, TaskScheduler.FromCurrentSynchronizationContext()); 
     task.Start(); 
    } 

Из предыдущего примера видно, как легко синхронизировать с UI потоке с помощью TaskScheduler.FromCurrentSynchronizationContext(), предполагается, что вы вызовите этот метод из потока пользовательского интерфейса.Задачи также обеспечивают оптимизацию для блокировки операций, таких как сценарии, в которых вам нужно ждать ответа службы и таких, предоставляя значение TaskCreationOptions.LongRunning для перечисления в конструкторе задач. Это гарантирует, что указанная операция не блокирует ядро ​​процессора, так как максимальное количество активных задач определяется количеством существующих процессорных ядер.