Обработка нескольких входов в фоновом режиме в приложении C # .NET4 - PullRequest
4 голосов
/ 24 декабря 2011

Я ищу подходящий шаблон и лучший современный способ решения следующей проблемы:

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

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

Мои выводы:

  1. ThreadPool.QueueUserWorkItem - проще понять, не очень удобно возвращать результаты
  2. BackgroundWorker - кажется, используется только для довольно простых задач, все рабочие работают в одном потоке?
  3. Асинхронный шаблон на основе событий
  4. Задачи в библиотеке параллельных задач
  5. C # 5 async / await - похоже, это ярлыки для задач из параллельной задачи

Примечания:

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

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

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

Ответы [ 3 ]

3 голосов
/ 07 января 2012

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

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

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

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

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

  1. ThreadPool.QueueUserWorkItem - чуть ли не худший вариант.Он может обрабатывать только фоновые операции (без событий) и плохо обрабатывает операции ввода-вывода.Возвращать результаты и ошибки можно вручную.
  2. BackgroundWorker (BGW) - не худшее, но определенно не лучшее.Он также обрабатывает только фоновые операции (без событий) и плохо обрабатывает операции ввода-вывода.Каждый BGW работает в своем собственном потоке - что плохо, потому что вы не можете воспользоваться преимуществом самобалансировки пула потоков.Кроме того, все уведомления о завершении (обычно) ставятся в очередь в один поток, что может стать причиной узкого места в очень загруженных системах.
  3. Асинхронный шаблон на основе событий (EAP) - это первая опция из вашего списка, котораябудет поддерживать асинхронные события, а также фоновые операции, а также может эффективно обрабатывать операции ввода-вывода.Тем не менее, это довольно сложно для правильного программирования, и у него та же проблема, что и в BGW, когда уведомления о завершении (обычно) все ставятся в очередь в один поток.(Обратите внимание, что BGW - это EAP, применяемый к фоновым операциям, связанным с процессором).Я написал библиотеку , чтобы помочь в написании компонентов EAP, а также некоторых сокетов на основе EAP.Но я не рекомендую этот подход;в эти дни доступны лучшие варианты.
  4. Tasks в Task Parallel Library - Task - лучший вариант для фоновых операций, как с привязкой к процессору, так и с вводом / выводом. Я просматриваю несколько вариантов фоновых операций в своем блоге - но эта запись в блоге вообще не затрагивает асинхронные события.
  5. C # 5 async / await - они позволяют более естественное выражениеиз Task фоновых операций.Они также предлагают простой способ синхронизации обратно в контекст вызывающего, если вы хотите (полезно для операций, инициируемых пользовательским интерфейсом).

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

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

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

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

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

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

2 голосов
/ 24 декабря 2011

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

1 голос
/ 24 декабря 2011

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

Вот краткий пример доступа к потоку пользовательского интерфейса из другого потока с помощью задач:

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();
    }

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

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