Как сообщить родителям, что поток выполняется на С++ с помощью pthreads?

У меня есть приложение TCP Server, которое обслуживает каждый клиент в новом потоке с использованием потоков POSIX и С++.

Сервер вызывает "прослушивание" своего сокета, а когда клиент подключается, он создает новый объект класса Client. Новый объект работает в своем потоке и обрабатывает запросы клиента.

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

Мой вопрос: как я могу сказать основному потоку, что поток выполняется?

8 ответов

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

В качестве альтернативы вы можете свернуть что-то с помощью некоторых общих переменных и мьютексов.


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

Очевидно, что если родительский поток выполняет другие действия, он не может постоянно блокироваться на pthread_join, поэтому вам нужно отправить сообщение в основной поток, чтобы сообщить ему позвонить pthread_join. Существует несколько механизмов IPC, которые вы могли бы использовать для этого, но в вашем конкретном случае (TCP-сервер) я подозреваю, что основной поток, вероятно, представляет собой цикл select, правильно? В этом случае я бы рекомендовал использовать pipe для создания логического канала и иметь дескриптор чтения для потока в качестве одного из дескрипторов, из которого выбирается основной поток.

Когда дочерний поток будет выполнен, он затем напишет какое-то сообщение в трубку, в котором говорится: "Я сделал!". и тогда сервер будет знать, чтобы вызвать pthread_join в этом потоке, а затем делать все, что ему нужно, когда заканчивается соединение.

Обратите внимание, что вам не нужно вызывать pthread_join в готовом дочернем потоке, если вам не требуется его возвращаемое значение. Однако, как правило, это хорошая идея, если дочерний поток имеет доступ к общим ресурсам, поскольку, когда pthread_join возвращается без ошибок, он заверяет вас, что дочерний поток действительно ушел, а не в каком-то промежуточном состоянии между отправкой "Я сделал!" сообщение и фактически вышло.


pthreads возвратите 0, если все пойдет хорошо или они вернут errno, если что-то не работает.

int ret, joined;
ret = pthread_create(&thread, NULL, connect, (void*) args);
joined = pthread_join(&thread, NULL);

Если joined равно нулю, поток выполняется. Очистите этот объект потока.


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

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

Если вы не хотите вызывать pthread_join() из основного потока из-за блокировки, вы должны отсоединить потоки клиента, установив его как параметр при создании потока или вызывающего pthread_detach().


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

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

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


Вызов pthread_join блокирует выполнение основного потока. Учитывая описание проблемы, я не думаю, что она предоставит желаемое решение.

Мое предпочтительное решение, в большинстве случаев, состояло бы в том, чтобы поток выполнял свою собственную очистку. Если это невозможно, вам придется либо использовать какую-то схему опроса с общими переменными (просто не забудьте сделать их потокобезопасными, намек: volatile), или, возможно, какой-то зависимый от ОС механизм обратного вызова. Помните, что вы хотите быть заблокированным при вызове listen, поэтому действительно подумайте о том, чтобы поток был очищен.


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

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


У меня была точно такая же проблема, как вы описали. После ~ 300 открытых клиентских подключений мое приложение Linux не смогло создать новый поток, потому что pthread_join никогда не вызывался. Для меня помогло использование pthread_tryjoin_np. Вкратце:

  • имеет карту, содержащую все дескрипторы открытых потоков
  • из основного потока до открытия нового клиентского потока. Я перебираю карту и вызываю pthread_tryjoin_np для каждого потока, записанного на карте. Если поток выполняется, результат вызова равен нулю, что означает, что я могу очистить ресурсы от этого потока. В то же время pthread_tryjoin_np заботится о выпуске ресурсов потока. Если вызов pthread_tryjoin_np возвращает число, отличное от 0, это означает, что поток все еще работает, и я просто ничего не делаю.

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

licensed under cc by-sa 3.0 with attribution.