Как сказать родителю, что поток выполняется в C ++ с использованием pthreads? - PullRequest
3 голосов
/ 22 марта 2010

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

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

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

Мой вопрос: как мне сообщить основному потоку, что поток завершен?

Ответы [ 8 ]

2 голосов
/ 22 марта 2010

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

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

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

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

2 голосов
/ 22 марта 2010

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

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

Если joined равен нулю, поток завершен. Очистить объект этого потока.

2 голосов
/ 22 марта 2010

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

Кроме того, вы можете свернуть что-нибудь с некоторыми общими переменными и мьютексами.

1 голос
/ 22 марта 2010

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

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

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

0 голосов
/ 09 мая 2012

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

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

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

0 голосов
/ 22 марта 2010

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

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

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

0 голосов
/ 22 марта 2010

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

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

0 голосов
/ 22 марта 2010

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

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

...