#c #linux #multithreading #sockets
#c #linux #многопоточность #сокеты
Вопрос:
Я внедряю файловый сервер, и он получает запросы от нескольких клиентов. Каждый клиент отправляет несколько запросов. На стороне сервера основной поток порождает новый рабочий поток для обработки запросов каждый раз, когда подключается новый клиент. Один рабочий поток обрабатывает все запросы от клиента, для которого он был создан. Итак, после того, как поток обработает запросы, он ожидает, что основной поток разбудит его, когда поступит другой запрос от того же клиента.
Я не уверен, как реализовать последнюю строку.вот как мне перевести поток в спящий режим и снова его разбудить.
Спасибо
Ответ №1:
Используйте переменные условия https://computing.llnl.gov/tutorials/pthreads/#ConVarSignal
Ответ №2:
Вы не «переводите поток в спящий режим». Поток переходит в спящий режим при выполнении любого блокирующего системного вызова.
Существует много способов реализовать то, что вы описываете, возможно, наиболее распространенным является «Шаблон пула потоков». Обычно это реализуется с использованием потокобезопасной очереди, которая, в свою очередь, может быть реализована с помощью мьютексов и переменных условий.
Рабочий поток перейдет в спящий режим, когда он ожидает от переменной условия (pthread_cond_wait), и ведущий разбудит его, сигнализируя о переменной условия (pthread_cond_signal).
Небольшой поиск должен выдать несколько примеров кода для очередей на основе pthread.
Ответ №3:
Сохраняют ли ваши потоки, обрабатывающие клиента, какое-либо состояние для клиента? Если это так, я могу понять, зачем вам нужен один выделенный поток для каждого клиента. Если это так, вы могли бы использовать очередь производитель-потребитель для каждого потока клиент-обработчик, как предложено Cnicutar и другими плакатами. Обычно я использую семафорное соединение с мьютексом, поскольку такая синхронизация доступна на всех платформах, но condvars в порядке. Неясно, как вы идентифицируете клиента при поступлении запроса на файл, поэтому управление клиентскими потоками может быть затруднительным или нет. Когда запрос на файл поступает в основной поток, как вы узнаете, в какую очередь его отправлять?
Rgds, Мартин
Ответ №4:
Вам следует изучить проблему производителей-потребителей. В двух словах вы хотите сделать что-то вроде этого:
- Рабочий поток пытается получить «задание» из очереди
- Если очередь пуста, поток блокируется (семафор / переменная условия), в противном случае он получает задание и обрабатывает его
- Главная очередь помещает задания в очереди и уведомляет работников («Эй, есть работа, которую нужно сделать»).
- Уведомленный рабочий мгновенно просыпается и пытается получить задание из очереди
Хитрость в том, чтобы сделать это действительно элегантно:
- get должен автоматически блокироваться, когда очередь пуста
- put должен автоматически сигнализировать о том, что рабочие ожидают его
Как бы вы это сделали с тем, что Linux предоставляет в ваше распоряжение?
Теперь вернемся к вашему вопросу. Кажется немного сомнительным, что вы запускаете поток для каждого клиента. Было бы намного проще, если бы у вас мог быть пул потоков, сидящий без дела и обрабатывающий запросы от любого клиента.
Комментарии:
1. Запуск потока для каждого клиента, безусловно, проще . Это может быть менее эффективно, но в современных реализациях я сомневаюсь, что разница где-либо так велика, как вы думаете.
2. @R.. Вероятно, вы правы. Тем не менее, я нахожу, что есть что-то тревожное в том, чтобы запускать потоки много раз в секунду и позволять им умирать. Опять же, с современными материалами (
clone
и тому подобным) запуск потоков для каждого клиента может быть вполне жизнеспособным.3. Мое случайное тестирование показывает, что
pthread_create
затраты примерно такие же, как при открытии/dev/null
2-4 раза. Конечно, это не обязательно учитывает какое-либо возможное влияние кэша / TLB, но если ваше приложение в значительной степени привязано к вводу-выводу и / или системному вызову в любом случае, это вряд ли будет иметь значение.
Ответ №5:
Я решил проблему с помощью потоков, созданных в начале клиентского подключения, и после этого поток получает и отправляет сообщения непосредственно клиенту, а не основному потоку, получающему и отправляющему сообщения после того, как рабочие потоки выполняют задачу.
Ответ №6:
У вас есть несколько возможностей, но я реализовал аналогичное многопоточное решение следующим образом:
- Разрешите методу вашего рабочего потока возвращаться, когда ему нечего делать
-
Когда ваш родительский поток получит запрос, добавьте его в очередь рабочего потока и вызовите метод «Wakeup» в соответствии с:
private void Wakeup() { if (!_workerThread.IsBusy) _workerThread.RunWorkerAsync(); }
Это позволит вам не переводить ваш поток в спящий режим, когда ему нужно будет регулярно проверять, было ли запрошено пробуждение. Это возлагает ответственность за пробуждение рабочего потока на родительский поток.
Комментарии:
1. Потоки не «зависают» в спящем режиме. И когда они находятся в спящем режиме, они не «регулярно проверяют, было ли запрошено пробуждение». Спящие потоки потребляют ровно ноль ЦП.