Основная мотивация для роста. Signals2 представляет собой версию оригинального Boost. Библиотека сигналов, которая может безопасно использоваться в многопоточной среде. Это достигается в основном за счет двух изменений от оригинального Boost. Сигналы API. Одним из них является введение новой схемы автоматического управления соединением, основанной на<shared_ptr>и<weak_ptr>, как описано в руководстве.. Вторым изменением стало введение параметра типа шаблона<Mutex>в класс<signal>. В этом разделе подробно описывается, как библиотека использует эти изменения для обеспечения безопасности потока и пределов обеспеченной безопасности потока.
Signals and combiners
Каждый сигнальный объект по умолчанию конструирует<Mutex>объект для защиты своего внутреннего состояния. Кроме того,<Mutex>создается каждый раз, когда к сигналу подключается новый слот, чтобы защитить связанное с ним соединение слот-сигнал.
Мутекс сигнала автоматически блокируется при вызове любого из методов сигнала. Мутекс обычно проводится до завершения метода, однако есть одно существенное исключение из этого правила. Когда сигнал вызывается вызовом<signal::operator()>, вызов сначала приобретает замок на мутексе сигнала. Затем он получает ручку к списку слотов и комбинатору сигнала. Затем он высвобождает мутекс сигнала, прежде чем вызывать комбинатор для повторения через список слотов. Таким образом, никакие мутексы не удерживаются сигналом, пока выполняется слот. Этот выбор дизайна делает невозможным для пользовательского кода, работающего в слоте, зайти в тупик против любого из мутексов, используемых внутри Boost. Библиотека Сигнал 2. Это также предотвращает случайные попытки блокировки слотов в любой из внутренних мутексов библиотеки. Поэтому, если вы вызываете сигнал одновременно из нескольких потоков, комбинатор сигнала может быть вызван одновременно, и, таким образом, слоты могут выполняться одновременно.
Во время вызова комбинатора выполняются следующие шаги, чтобы найти следующий вызывающий слот при итерации через список слотов сигнала.
<Mutex>, связанный с соединением со слотом, заблокирован.
Все отслеживаемые<weak_ptr>, связанные со слотом, копируются во временные<shared_ptr>, которые будут сохранены до тех пор, пока вызов не будет сделан с слотом. Если это не удается из-за истечения срока действия<weak_ptr>, соединение автоматически отключается. Ведь если бы не было слота, то не было бы слота, если бы не истек срок его действия<weak_ptr>, и ни один из его отслеживаемых<weak_ptr>не истек бы, пока слот работает.
Подключение слота проверяется, заблокировано ли оно или отключено, а затем разблокируется мутекс соединения. Если соединение было заблокировано или отключено, мы начинаем сначала со следующего слота в списке слотов. В противном случае мы обязуемся выполнить слот, когда комбайнер следующий относит слот к итератору (если комбинатор не должен увеличивать итератор, никогда не отменяя его).
Обратите внимание, что, поскольку мы разблокируем мутекс соединения перед выполнением связанного с ним слота, возможно, слот все еще будет выполняться после того, как он был отключен<connection::disconnect()>, если отключение было вызвано одновременно с вызовом сигнала.
Вы, возможно, заметили выше, что во время вызова сигнала вызов получает только ручки в слот-лист сигнала и комбинатор, удерживая мутекс сигнала. Таким образом, одновременные вызовы сигнала могут по-прежнему получать доступ к одному и тому же списку слотов и комбинатору одновременно. Итак, что произойдет, если список слотов будет изменен, например, путем подключения нового слота, в то время как вызов сигнала происходит одновременно? Если список слотов уже используется, сигнал выполняет глубокую копию списка слотов перед его изменением. Таким образом, одновременный вызов сигнала будет продолжать использовать старый неизмененный список слотов, не нарушаемый изменениями, внесенными в недавно созданную глубокую копию списка слотов. Будущие вызовы сигнала получат ручку к недавно созданной глубокой копии списка слотов, а старый список слотов будет уничтожен, как только он больше не будет использоваться. Аналогично, если вы измените комбинатор сигнала с<signal::set_combiner>, пока вызов сигнала работает одновременно, вызов одновременного сигнала будет продолжать использовать старый комбинатор без помех, в то время как будущие вызовы сигнала получат ручку к новому комбинатору.
Тот факт, что одновременные вызовы сигналов используют один и тот же объект комбинатора, означает, что вам нужно застраховать любой пользовательский комбинатор, который вы пишете, является безвредным. Поэтому, если ваш комбинатор поддерживает состояние, которое изменяется при вызове комбинатора, вам может потребоваться защитить это состояние с помощью мутекса. Имейте в виду, что если вы держите мутекс в своем комбинаторе во время итераторов вызова слота, вы рискуете зайти в тупик и рекурсивную блокировку, если какой-либо из слотов вызывает дополнительную блокировку мутекса. Один из способов избежать этих опасностей заключается в том, чтобы ваш комбинатор высвободил любые замки, прежде чем отклонить итератор вызова слота. Классы комбайнов, предоставляемые Boost. Библиотека Signals2 безопасна для потоков, поскольку не поддерживает никакого состояния во время вызовов.
Предположим, что пользователь пишет слот, который соединяет другой слот с вызывающим сигналом. Будет ли новый подключенный слот работать во время того же вызова сигнала, в котором было сделано новое соединение? Ответ - нет. Подключение нового слота изменяет список слотов сигнала, и, как описано выше, уже в процессе вызова сигнала не будет никаких изменений, внесенных в список слотов.
Предположим, что пользователь пишет слот, который отключает другой слот от сигнала вызова. Будет ли отключенный слот не работать во время того же вызова сигнала, если он появится позже в списке слотов, чем слот, который его отключил? На этот раз ответ – да. Даже если отключенный слот все еще присутствует в списке слотов сигнала, каждый слот проверяется, отключен ли он или заблокирован непосредственно перед его выполнением (или не выполнен, в зависимости от случая), как было описано более подробно выше.
Connections and other classes
Методы класса<signals2::connection>являются потоково-безопасными, за исключением назначения и обмена. Это достигается путем блокировки мутекса, связанного с базовым соединением сигнал-слот объекта. Назначение и обмен не являются нитями-безопасными, потому что mutex защищает основное соединение, на которое ссылается объект<signals2::connection>, а не сам объект<signals2::connection>. То есть может быть много копий<signals2::connection>объекта, все из которых ссылаются на одну и ту же базовую связь. Для каждого<signals2::connection>объекта нет мутекса, есть только один мутекс, защищающий базовое соединение, на которое они ссылаются.
Класс<shared_connection_block>получает некоторую защиту потока от<Mutex>, защищающего основное соединение, которое блокируется и разблокируется. Внутренний подсчет ссылок, который используется для отслеживания того, сколько<shared_connection_block>объектов утверждают блоки на их базовом соединении, также безопасен для потоков (реализация опирается на<shared_ptr>для подсчета ссылок). Однако к отдельным<shared_connection_block>объектам не следует обращаться одновременно несколькими потоками. До тех пор, пока две нити имеют свой собственный<shared_connection_block>объект, они могут использовать их в безопасности, даже если оба<shared_connection_block>объекта являются копиями и относятся к одному и тому же базовому соединению.
Класс<signals2::slot>не имеет встроенной внутренней блокировки мутекса. Ожидается, что слот-объекты будут созданы, а затем подключены к сигналу в одной нити. После того, как они были скопированы в список слотов сигнала, они защищены мутексом, связанным с каждым соединением слота сигнала.
Класс<signals2::trackable>НЕ обеспечивает безопасное автоматическое управление соединением. В частности, он оставляет открытой возможность вызова сигнала, вызывающего частично разрушенный объект, если объект отслеживаемого происхождения разрушается в потоке, отличном от того, который вызывает сигнал.<signals2::trackable>предоставляется только в качестве удобства для переноса однопоточного кода с Boost. Сигналы на Boost.Signals2
Последний пересмотр: 12 июня 2007 года в 14:01:23 -0400
Статья Thread-Safety раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 33. Boost.Signals2 может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.