C++, что и/или где выполняется pthread?

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

Итак, мне нужно выяснить, где в данный момент выполняется поток (ожидает ли он в настоящее время условно или выполняет что-то). Может ли кто-нибудь предложить способ сделать это?


person user1172252    schedule 16.02.2012    source источник


Ответы (4)


Если поток выполнения простаивает, не будет ли он ждать в очереди? Самый быстрый способ заставить его работать — это, вероятно, просто поставить работу в очередь.

Есть ли у вас основания полагать, что очередь является узким местом?

person Derek Park    schedule 16.02.2012
comment
«Если поток выполнения простаивает, не будет ли он ждать в очереди?» - не обязательно - у меня есть хитрый план.. :) - person Martin James; 17.02.2012

Это то, что очередь уже должна делать.

Во-первых, поток не может бездействовать, если очередь не пуста, верно?

Так что же делает ваша операция «постановка в очередь и сигнал»? Он помещает указатель на данные, где поток может их найти, а затем указывает потоку работать с данными. Это минимальная задача, чтобы сделать то, что вы хотите сделать в любом случае.

Так что оптимизация невозможна.

person David Schwartz    schedule 16.02.2012
comment
Оптимизация возможна и действительно обеспечивает некоторый прирост производительности. Я не могу вспомнить, сколько - я использовал дизайн OP в очереди Delphi несколько десятилетий назад. Я помню, что он превзошел очереди на основе семафоров, очереди сообщений Windows и очереди IOCP (которые мучительно медленны при использовании для простых межпоточных коммуникаций). - person Martin James; 17.02.2012
comment
Если оптимизация возможна, она должна быть встроена в структуру очереди, а не рассматриваться как отдельный путь отправки. - person David Schwartz; 17.02.2012
comment
да, должна быть возможность просто создать экземпляр такой очереди как потомка общей очереди - без глобальных переменных, без внешних флагов. - person Martin James; 17.02.2012

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

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

person Tudor    schedule 16.02.2012
comment
Глобальные флаги, кроме «глобального» аспекта, не работают, если не защищены блокировкой, после чего все становится грязным. Проверки состояния потока и другое подобное микроуправление почти всегда дают сбой. - person Martin James; 17.02.2012
comment
@MartinJames Это будет защищено блокировкой, потому что вы не можете вызвать pthread_cond_wait, если у вас нет мьютекса, который защищает общее состояние. Но я согласен с вами обоими, что такое микроуправление вряд ли будет продуктивным. - person David Schwartz; 17.02.2012

Вы можете сделать это, но хотите ли вы на самом деле, это другой вопрос - см. другие сообщения.

Во-первых, забудьте обо всех потребительских потоках, ожидающих общего семафора. Чтобы сделать то, что вы, кажется, хотите, ожидающие потребительские потоки должны быть адресованы экземпляром. Для этого потребитель, который появляется, блокирует очередь и обнаруживает, что она пуста, должен дождаться собственного события. Кроме того, потребитель должен предоставить в своем вызове pop адрес места, куда он хочет поместить объект. Таким образом, в дополнение к «обычной» очереди объектов, потоки-потребители, которые должны ожидать, нуждаются в структуре, содержащей указатель и событие для ожидания. Вы можете создать массив или циклический буфер этих структур ожидания при создании очереди ПК.

Тогда все готово.

PRODUCER: (вызывает push с объектом ref/ptr) Получает блокировку очереди и проверяет список структур ожидания. Если есть запись, он загружает свой объект по адресу, указанному указателем wait_struct (то есть «отправляет новую продукцию () непосредственно в поток выполнения»), и сигнализирует о событии wait_struct. Если в списке wait_structs нет записи, производитель помещает свой объект в очередь объектов. Ах да - снимает блокировку очереди :)

ПОТРЕБИТЕЛЬ: (вызывает pop с адресом, куда он хочет поместить ссылку на объект) Получает блокировку очереди и проверяет количество объектов в очереди. Если он не равен нулю, он извлекает объект, помещает его на указанный целевой адрес, снимает блокировку и продолжает работу. Если очередь объектов пуста, потребитель получает свободный объект wait_strut в списке структур ожидания, устанавливает указатель на значение, которое он передал, снимает блокировку очереди и ожидает события. Когда событие получает сигнал, потребитель уже имеет свой объект (вставленный производителем) и может просто работать - нет необходимости снова посещать очередь ПК.

Да, этот дизайн работает (во всяком случае, в Delphi - должен работать в C++) и быстрее, чем "классическая" очередь ПК на основе семафоров (которая быстрее, чем очередь сообщений Windows, которая быстрее, чем очередь IOCP). ).

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

person Martin James    schedule 17.02.2012
comment
Это интересная идея, но как это быстрее? Путь вторичного кода запускается только в том случае, если очередь пуста. Если очередь пуста, значит, ваши рабочие процессы простаивают, и общая пропускная способность не изменится существенно, если они пропустят очередь при выполнении следующего рабочего элемента. Если ваши работники простаивают, вам нужно дать им больше работы, а не заставлять их ждать быстрее. - person Derek Park; 17.02.2012
comment
Это то, что вы просили «отправить новый продукт () непосредственно в потоки выполнения, если они простаивают (вместо того, чтобы ставить его в очередь в очередь». Это быстрее из-за меньшего количества вызовов ядра по сравнению с «классическим ' PC-queue. Если защитой очереди является фьютекс/критическая секция, никакие вызовы ядра абсолютно не требуются в случаях, когда производители добавляют объекты в очередь объектов (т. е. нет ожидающих потребителей), а потребители берут объекты из очереди, ( потребителю не нужно ждать). - person Martin James; 17.02.2012
comment
@DerekPark - «Если ваши работники бездействуют, вам нужно дать им больше работы» - я не могу этого сделать, если им нечего делать. Они бездействуют, потому что нет работы. Если бы была работа, они бы не сидели без дела. Единственная дальнейшая оптимизация, о которой я мог подумать, требует обширных частных данных для каждого потока, т.е. каждый поток, который хотел бы использовать очередь, должен был сначала «войти в систему», чтобы получить свой частный объект интерфейса очереди. Это слишком запутанно для обычной очереди ПК - она ​​становится менеджером threadPool. - person Martin James; 17.02.2012
comment
Я не ОП и не спрашивал, как отправить рабочим напрямую. Мой вопрос в том, как ваше предложение на самом деле быстрее. Я понимаю, что концептуально вы можете избежать вызова ядра с вашим подходом. Но это не имеет значения в макромасштабе. Если ваши работники простаивают, то попытка оптимизировать скорость передачи рабочих элементов не улучшит пропускную способность. И если ваши воркеры не сидят без дела, то в очереди будут элементы, и ваш альтернативный путь фактически не будет работать. - person Derek Park; 17.02.2012
comment
«Я не ОП и не спрашивал, как отправить рабочим напрямую» - моя вина, извините. Вы правы в отношении других проблем — значительного прироста производительности следует ожидать только в тех случаях, когда потребители простаивают в течение коротких периодов времени, а задержка важна. В системе стресс-тестирования очереди ПК со 150 производителями и 150 потребителями задержка важна для обеспечения высокой пропускной способности. В 99% всех бытовых приложений это просто не так важно, поэтому я обычно использую простую очередь семафоров. - person Martin James; 17.02.2012