Nano Hash - криптовалюты, майнинг, программирование

Есть ли слишком много потоков?

Среда: Ubuntu 16.04 - Linux, компиляция C ++ 11 с использованием GCC. Программное обеспечение не обязательно должно быть кроссплатформенным - оно должно эффективно выполнять свою задачу и быть отличным демоном.

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

| End-User |  <-C1-> | My Application | <-C2-> | 3rd Party Service |

В настоящее время мое приложение имеет два основных потока:

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

  2. Поток 2 - проходит через очередь сообщений, выводит сообщение и обрабатывает его.

Проблема в том, что Thread 1 довольно быстр и может легко обрабатывать сотни подключений к веб-сокетам. Поток 2 время от времени блокирует задачи и может быть медленным, поскольку обработка определенных элементов очереди занимает некоторое время указанной сторонней службой. Это означает, что если пользователь A запрашивает 1, ответ на который занимает 5 секунд, то пользователь B, который пришел позже и сделал запрос 2, должен будет дождаться завершения запроса 1 пользователя A, даже если запрос 2 занимает менее 1 мс.

Предлагаемое мной решение этой проблемы:

  • Поток 1 - прослушиватель подключения WebSocket
  • Делегатор задач потока 2
  • Тема 3 - 100 - Рабочие задачи

Поток 1 может легко обрабатывать сотни подключений к веб-сокетам, и каждое соединение может выполнять запросы задачи, которые могут занимать от 1 мс до 1 минуты. все нити 3 - 100 спят. Причина такого большого количества потоков заключается в том, что при наличии 50-60 соединений, которые все выполняют разные длительные запросы, каждый из этих трудоемких вызовов блокирует только один поток, другие потоки по-прежнему могут работать с очередью, и делать другие задачи.

Я знаю, что переключение потоков - это интенсивная операция, но я не знаю другого подхода, кроме многопоточности.

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

Звучит как ужасная дизайнерская идея? Есть мысли о лучших практиках?

12.02.2018

  • Да, у вас могут закончиться ручки. Также количество (активных) потоков должно быть сбалансировано с количеством доступных ядер ЦП. 12.02.2018
  • Возможно, вы захотите найти пулы потоков. 12.02.2018
  • Количество потоков не будет проблемой: stackoverflow.com/questions/481900/ 12.02.2018
  • Еще одно хорошее руководство по эффективной организации очередей обмена сообщениями - ZeroMQ. 12.02.2018
  • Потоки используют системные ресурсы, которые ограничены, поэтому всегда возможно иметь слишком много потоков. Пределы зависят от системы. В любом случае ваша идея имеет много общего с шаблоном проектирования программного обеспечения, известным как пул потоков. Возможно, вам придется рассмотреть возможность использования асинхронного ввода-вывода, если ваша хост-система поддерживает его. 12.02.2018
  • Ваши потоки 3-100 обычно называются пулом потоков. Это простое решение для неблокирующего ввода-вывода. Если вы хотите масштабировать вещи и знать, какие запросы займут 5 секунд, вы можете создать 2 (или более) пула. Один для коротких запросов и один для длинных. В противном случае вы в какой-то момент закончите тем, что все потоки будут заблокированы длинными запросами. Это также может удерживать короткие потоки запросов в кеше. 12.02.2018
  • @GoswinvonBrederlow - да, ленивый мужик неблокирующий io. если я использую пул потоков, и большинство задач просто ждут ввода-вывода, тогда весь пул может быть израсходован. и мне может просто потребоваться увеличить размер пула до 100, чтобы учесть ожидаемую блокировку. это означает, что мы вернулись к большому количеству обсуждений .. не так ли? 12.02.2018
  • @Peter linux поддерживает async io, но API, который я использую для подключения C2 из моего приложения, не использует асинхронные сокеты. они написаны с использованием устаревшего старого кода ... тщательно протестированного, но старого. 12.02.2018
  • «Делегатору задачи» не нужно. 12.02.2018
  • «один рабочий процесс в очереди задач работает медленно, поскольку он последовательно обрабатывает блокирующий ввод-вывод» ОК, сначала попробуйте - просто добавьте фиксированное количество рабочих потоков, читающих из очереди (которая должна быть «реальной» очередью производителя-потребителя, т. е. .блоки пустые) .. Может быть, это просто добавление однострочного цикла for, чтобы попробовать это :) Попробуйте это 100 .. 12.02.2018
  • @mercy У вас есть только два варианта. Потоки или неблокирующий ввод-вывод. Когда вы используете все потоки в пуле, следующий запрос будет заблокирован (если вы не измените размер пула). Я использовал два пула, чтобы убедиться, что быстрые запросы ждут только завершения другого быстрого запроса, в то время как длинные запросы должны ждать завершения других длинных запросов. Длинные запросы обычно также означают большую загрузку процессора или дискового ввода-вывода. Вы хотите ограничить их, не блокируя быстрые запросы. 15.02.2018

Ответы:


1

Есть ли слишком много потоков?

да

100 потоков

Должно быть хорошо, хотя и неоптимально на любом рабочем столе / сервере. У меня был ноутбук, который отказывался продолжать работу после ~ 2000 потоков в процессе.

Другие стратегии

Для максимальной пропускной способности обычное проектное решение составляет ~ 1 поток на ядро ​​ЦП при конструкции с асинхронным реактором.

Некоторые примеры:

  • libuv

  • boost :: asio

  • libdispatch

  • асинхронные операции win32

12.02.2018
  • 1 поток на ядро ​​подходит для задач с узким местом ЦП; но когда проблема связана с вводом-выводом, это намного более расплывчато. Я работал над приложением с сотнями потоков, которые просто выполняли задачи проверки связи; потому что чаще всего поток находится в блокирующей функции, ожидая сетевых данных. 12.02.2018
  • @UKMonkey - это то, что проекты на основе реакторов стремятся смягчить. Но да, мы все там были ... 12.02.2018
  • Я считаю, что стоит упомянуть подход ZeroMQ. Особенно, когда речь идет о шаблонах проектирования MQ, таких как реакторы. 12.02.2018
  • Некоторые вещи, на которые стоит обратить внимание: асинхронный ввод-вывод POSIX (который, кстати, использует потоки под Linux), select / poll / epoll, неблокирующие сокеты 12.02.2018

  • 2

    "Может быть, слишком много ниток?" - Да. Потоки потребляют системные ресурсы, которые могут иссякнуть. Необходимо запланировать потоки; требует работы ядра, а также времени на ЦП (даже если они потом не захотят ничего делать). Чем больше потоков, тем сложнее становится сложность вашей программы, что затрудняет анализ и отладку.

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

    12.02.2018
  • Нет необходимости планировать потоки, если они не готовы / не запущены. Коробка, на которой я это размещаю, управляет банкоматом с 1044 потоками. Совершенно никаких проблем. В настоящее время загрузка процессора составляет 2-3% (в основном utorrent и Firefox). 12.02.2018
  • @Martin James, конечно, но ядру все равно пришлось учитывать их. Они по-прежнему занимают место в очереди выполнения и по-прежнему занимают память. 12.02.2018
  • 'требует работы ядра, а также времени на ЦП (даже если они затем решают ничего не делать' ', продолжайте, попробуйте. Сделайте 1000 потоков, которые, скажем, циклически обходят длинный вызов сна. Подождите немного для всех этих потоки, которые нужно создать, и посмотреть, что произойдет с производительностью вашего бокса [ничего примечательного]. 12.02.2018
  • «Они по-прежнему занимают место в очереди на выполнение» - нет. «они все еще занимают память» - их стеки, код могут быть выгружены, как и любая другая выгружаемая память. Пока у вас не исчерпывается виртуальная память, пытаясь создать тысячи потоков с огромными ограничениями стека, неготовые потоки просто как бы «исчезают». 12.02.2018

  • 3

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

    И Windows, и Linux поддерживают пулы потоков, но, поскольку вы используете C ++ 11, вы также можете использовать пул потоков стандартной библиотеки C ++, который можно использовать, вызвав функцию std::async. Вот хороший образец.

    13.09.2018
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..