Как сообщить о событии нескольким потокам - PullRequest
3 голосов
/ 20 октября 2011

Как я могу отправить событие нескольким запущенным потокам моего приложения?

Например : Мой основной поток хочет сообщить всем запущенным потокам о выходе из приложения.

Я немного запутался, какая из этих возможностей приводит к простому и надежному решению:

  • condition_variable с notify_all из boost
  • CONDITION_VARIABLE с WakeAllConditionsVariable из WinApi
  • CEvent от WinApi
  • с использованием mutex и try_lock в потоках
  • signal и slots от boost
  • или даже глобальная переменная с CriticalSection для защиты геттера / установщика
  • введите ваше решение здесь ...

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

Добавлено: - ОС Windows XP

Ответы [ 4 ]

2 голосов
/ 20 октября 2011

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

Если вы намерены делиться задачами /Для событий между потоками рекомендуется использовать шаблон производитель-потребитель , основанный на очереди с мьютексом и условной переменной.Типичный пример: у вас есть один поток производителя , который создает задания, помещаемые в очередь (например, запись данных в другой файл для каждой задачи).Тогда у вас может быть несколько пользовательских потоков , которые будут удалять задачу из очереди и воздействовать на них.Для связи между потоками вы можете иметь условную переменную , которая будет сигнализировать потокам потребителя, что очередь мьютекса может быть пустой .(Ознакомьтесь с переменными условия, чтобы понять, почему я говорю «может» здесь).

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

Последнее замечание: это немного выходит за рамки вашего вопроса, но вы также почувствуете, сколько потоков вам действительно нужно порождать.Переключение контекста между потоками относительно дешево, но стоимость определенно не равна нулю.Это означает, что при порождении большего количества потоков существует точка уменьшения отдачи.Подумайте об этом с точки зрения ОС: алгоритм должен тратить циклы, чтобы решить, когда переключать контекст в дополнение к фактическому переключению.Эта стоимость не равна нулю.Таким образом, другой тип оптимизации, который вы найдете при выполнении большего количества потоковых программ, - это когда нужно выполнять в потоке как можно больше работы, не дожидаясь, пока другой поток выполнит эту работу.Конечно, вы должны найти баланс между чистотой вашего дизайна и оптимизацией по скорости.Об этом нужно подумать при разработке приложения.

2 голосов
/ 20 октября 2011

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

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

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

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

2 голосов
/ 20 октября 2011

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

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

  • Если у вас есть ссылки на объекты, которые управляют потоками, вы можете использовать их для ручного обхода списка потоков и уведомления каждого из них
  • Вы можете использовать boost :: signal в какой-то момент в программе для управления этими ссылками (то есть создать сигнал exit и соединить все потоки в процессе построения.
  • Если у вас нет этих ссылок, вы можете установить флаг exit как глобальную переменную и читать потоки из нее

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

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

  • мьютекс. Каждый поток блокируется перед чтением / записью, гарантируя, что изменения безопасны
  • Атомные операции. Для достаточно малого типа данных библиотека / операционная система потоков предлагает атомарные операции, которые добавят соответствующие барьеры памяти, чтобы обеспечить перемещение данных по

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

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

1 голос
/ 20 октября 2011
  1. boost::condition_variable ожидаю std::condition_variable C ++ 11. На сегодняшний день (2011) реализован полностью в системах с интерфейсом POSIX (например, UNIX / LINUX), но имитируется только для окон (с частичной поддержкой)
  2. windows CONDITION_VARAIBLE - это «аппаратное обеспечение», необходимое для реализации std::condition_varable в окнах с полной поддержкой. Но существует только с win6 (Vista и 2008). Мир по-прежнему разделен множеством XP (поэтому mingw -pthread не поддерживается в Windows).
  3. Объекты Winapi evvent (см. CreateEvent) могут имитировать условную переменную posix, но они не совсем одинаковы (в POSIX условная переменная может использоваться для ослабления только одного или всех в каждом конкретном случае. В Windows создается событие быть автоматическим (и в случае пробуждения одного потока и автоматического сброса) или ручным (и в случае слабого сигнала все ждет, пока не останется сигнал. Сброс должен быть ручным) и не может изменять поведение в каждом конкретном случае.
  4. Забыл сигнал и слоты: их цель не в том, чтобы «ослабить поток ожидания, когда что-то происходит». Это просто совершенно другое (не связанное) понятие.
  5. CriticalSection - это версия «одного процесса» для более общего Mutex (то же самое для Windows и POSIX, с той разницей, что Windows не различает рекурсивный и нерекурсивный: все рекурсивные).

Тем не менее, за событиями (и условной переменной) и мьютексом (и критическими секциями) существует другая область работы:

Мьютексы - по существу - защищают код, который нельзя запускать одновременно. По сути, они означают «если этот фрагмент кода уже выполняется, подождите, пока другой завершит его».

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...