Запустить функцию при выходе из pthread - PullRequest
2 голосов
/ 08 марта 2012

У меня есть приложение на C ++, в котором я создаю pthreads для запуска пользовательских функций. Я хочу быть в состоянии каким-либо образом получать уведомления при выходе из потока, чтобы я мог удалить его из массива pthread, который я использую для сохранения потоков. Есть ли способ сделать это, или функция должна просто установить какое-то «магическое значение». Поскольку мой основной код, который порождает pthreads, находится в некотором роде runloop, я легко могу проверить условие выхода.


Кроме того, используется ли перегрузка std::vector<pthread_t> для отслеживания перегрузки моих потоков? Количество потоков не обязательно является каким-либо постоянным, может быть запущено много или очень мало потоков. Или есть другой контейнер STL, который был бы хорош для этих добавлений и удалений (добавления всегда на одном конце, удаления почти в любом месте). Есть ли какая-то другая структура для отслеживания pthreads? Будет ли стек или список прямо здесь? Или стандартный массив C с максимальным благом? Из-за характера проблемы я также мог бы поддерживать массив рабочих потоков фиксированного размера, которому я передаю пользовательские функции, которые должны быть выполнены. Это хорошее решение?

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


РЕДАКТИРОВАТЬ (3/08/12): Прочитав ответ @ jojojapan, я решил использовать своего рода пул потоков. В моей структуре у меня есть один производитель (поток в цикле выполнения) и много потребителей (рабочие потоки в пуле). Существует ли структура данных, предназначенная для многопоточного многопользовательского использования одного производителя? Или просто использовать std::queue с pthread_mutex_t на нем?

Ответы [ 3 ]

3 голосов
/ 08 марта 2012
  1. Один из вариантов, который вы можете рассмотреть, заключается в том, чтобы на самом деле не завершать и удалять потоки, как только они закончили задачу, а вместо этого сохранить их живыми и заставить их ждатьновое задание, которое им будет назначено. Это можно сделать двумя способами:

    1. Использовать (почти) бесконечный цикл в потоке
    2. Использовать параллельную очередь иликакая-то другая техника, которая заставляет их ждать сигнала от другого потока.Шаблоны и стратегии проектирования обсуждаются в нескольких вопросах SO, например, , этот
  2. Если вы действительно хотите отправить сигнал один разпоток заканчивается, вы можете использовать pthread_cond_t и вызвать pthread_cond_signal непосредственно перед тем, как поток достигнет своего оператора return.Конечно, это предполагает, что существует какой-то другой поток, который ожидает эти сигналы и воздействует на них, удаляя соответствующий поток из вектора.Подробная информация об использовании описана на соответствующей странице руководства, но также и в этой публикации SO .

Редактировать , связанной с комментарием иОтредактированная часть вопроса:

  1. Относительно количества рабочих потоков: это зависит от ресурсов, наиболее используемых потоками.Если эти потоки в основном выполняют вычисления и немного обращаются к памяти, другими словами, если они связаны с процессором, имеет смысл использовать столько потоков, сколько поддерживает ваш процессор (в частности, имеется определенное количество ядер,и количество (аппаратных) потоков на ядро, которое ваш ЦП может запустить, прежде чем они начнут замедлять друг друга. Потоков, которые вы создаете (программные потоки), должно быть примерно столько же, или, возможно, несколько больше (до двух раз больше, чемаппаратные потоки разумны в соответствии с , что @Tudor говорит здесь )).Однако, если ваши потоки интенсивно используют память (связанную с памятью) или жесткий диск (связанный с вводом-выводом) или другие ресурсы, такие как сеть, NFS или какой-либо другой сервер, вы можете уменьшить количество потоков по порядку (a) не заставлять их блокировать друг друга, и (б) не создавать чрезмерную нагрузку на определенные ресурсы.Определение правильного количества потоков может быть вопросом эксперимента, и поддержание настраиваемого числа, как правило, является хорошей идеей.

  2. Относительно наилучшей структуры данных для хранения рабочих задач: параллельная ограниченная очередь , упомянутая в комментариях к посту, который я цитировал выше, вероятно, очень хороша.Я сам не пробовал.Но если вы хотите сохранить простоту, стандартный std::queue или даже просто std::vector не будет плохим выбором, если вы защитите их должным образом, используя технику сигнал / мьютекс.

1 голос
/ 08 марта 2012

Простой способ сделать это - просто использовать трубу.

Откройте трубу перед порождением нитей. Передайте канал fd как часть данных вашего потока. Перед тем как выйти из потока, запишите его pthread_self() в трубу. Имейте главную или отдельную резьбу на конце чтения трубы. Он читает tid мертвой нити и немедленно выполняет pthread_join. (Если это отдельная нить жнеца, он может просто заблокировать чтение канала; если он в основном, просто сделать его частью вашего выбора / опроса или чего-то еще.)

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

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

1 голос
/ 08 марта 2012

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

Boost.thread pool - один из многих, ссылка .

...