Подтвердить что ты не робот

С# - Когда использовать стандартные потоки, ThreadPool и TPL на высокопроизводительном сервере

В последнее время я много читаю о потоковой передаче, так как я ищу для разработки высокопроизводительного масштабируемого TCP-сервера, способного обрабатывать до 10 000-20 000 клиентов, каждый клиент которого последовательно связывается с сервером двунаправленно с помощью командная система. Сервер получит команду и выполнит либо одну (или многие) задачи в соответствии с командой. Мой вопрос заключается в том, как правильно использовать конструкторы Threading.NET для различных ситуаций, выполняя задачи, которые могут занимать от одной минуты до нескольких часов, в зависимости от выполняемой работы.

Что меня больше всего сбивает с толку, так это то, что везде, где я читаю, я вижу нечто вроде "использовать созданный вручную Thread (или собственный пул потоков) для обработки" длительных "задач и использовать TPL для краткосрочных задач, или задачи, требующие параллельной обработки". Что такое долгосрочная задача? Это 5 секунд, 60 секунд, час?

С какими временными рамками я должен использовать каждый из этих трех методов создания потоков:

  • Созданные вручную темы
  • Класс .NET ThreadPool
  • TPL

Еще одна проблема, о которой я подумал, заключается в следующем: скажем, на моем сервере есть 20 000 клиентов, каждая из которых отправляет 1 команду (которая может переводить на одну или несколько задач) в секунду. Даже с мощным аппаратным обеспечением, нет ли шанса, что я мог бы слишком сильно загружать рабочую нагрузку в любую очередь пула потоков/рабочих элементов, которые у меня есть, тем самым, в результате генерируя исключение OutOfMemoryException после того, как очередь будет медленно заполняться до максимума?

Любое понимание будет принята с благодарностью.

4b9b3361

Ответ 1

Собственно, для этого сценария все из них являются вторичными; первое, на что вы должны обратить внимание, это asyc-IO, aka .BeginRead(...) и т.д.; это позволяет свести к минимуму количество потоков, ожидая портов ввода-вывода - гораздо эффективнее.

После того, как у вас есть полное сообщение, на этом уровне я бы бросил сообщение в пользовательский пул потоков/синхронизированную очередь. У меня было бы контролируемое количество регулярных потоков (не пул потоков или IOCP), обслуживающих эту очередь для обработки каждого элемента.

Как бы то ни было, сейчас я делаю что-то подобное (нижняя шкала); для предотвращения взрыва памяти я закрыл рабочую очередь; если он заполняется (т.е. работники не могут идти в ногу со временем), вы можете блокировать IOCP в течение небольшого времени, возможно, с тайм-аутом, который в конечном итоге говорит клиенту "слишком занят" на уровне IOCP.

Ответ 2

Что меня больше всего сбивает с толку факт, что везде, где я читаю, я вижу что-то вроде "использовать вручную созданные Thread (или пользовательский пул потоков) для обрабатывать" длительные "задачи и использовать TPL для краткосрочных задач или задач которые требуют параллельной обработки."

Странный совет, или, может быть, вы немного процитировали. Поток также способен выполнять параллельную обработку, а с помощью TPL вы можете создать задачу с параметром LongRunning. Остается то, что вы не должны запускать длинные задачи в ThreadPool.

Что такое долгосрочная задача? Это 5 секунд, 60 секунд, час?

TPL запускается поверх ThreadPool, и TP создаст новые потоки со скоростью не более 2 в секунду. Так долго работает >= 500 мс


Даже с мощным оборудованием, это не есть шанс, что я мог бы толкать слишком высокая рабочая нагрузка нить пул/очередь рабочих элементов у меня есть,

Да, инструмент Threading не может расширить вашу фактическую емкость...

С 20-тысячными клиентами вам, вероятно, понадобится серверная ферма, которая будет включена в ваш дизайн раньше...

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

Ответ 3

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

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

Сервер будет помещать все запросы в очередь сообщений, которые считываются несколькими другими серверами. Эти серверы обрабатывают запросы и помещают ответ в другую очередь сообщений, которая считывается первым сервером.

Другим решением будет использование сервера балансировки нагрузки.

Ответ 4

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

Как правило, делайте рабочие нагрузки потоков достаточно короткими, чтобы завершить максимум за несколько секунд. Что-нибудь еще, вы начнете использовать ресурсы сервера и серьезно повлиять на масштабируемость вашего сервера. Имея десятки тысяч потоков, блокирующихся при длительных операциях или выполняющих эти длительные операции одновременно, определенно убьет вашу масштабируемость.

Не уверен, сколько процессорного времени вы потребляете в каждом долгом. Это повлияет на ваш дизайн, например:

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

Если каждая долгожданная операция ожидает завершения других операций, рассмотрите Windows Workflow Foundation.

Если каждая длительная работа потребляет процессор, вы не хотите, чтобы слишком много их было запущено в любой момент времени, или оно будет разбивать ваш сервер. В этом случае используйте MSMQ и/или TPL для постановки задач в очередь и убедитесь, что только несколько из них работают одновременно.

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