Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Condition Variables

Boost , Chapter 1. Fiber , Synchronization

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext
Synopsis
enum class cv_status; {
    no_timeout,
    timeout
};
class condition_variable;
class condition_variable_any;

Класс<condition_variable>обеспечивает механизм для волокна, чтобы ждать уведомления от другого волокна. Когда клетчатка пробуждается от ожидания, она проверяет, является ли соответствующее состояние верным, и продолжается, если это так. Если это не так, то волокно снова вызывает<wait>, чтобы возобновить ожидание. В простейшем случае это условие является просто булевой переменной:

boost::fibers::condition_variable cond;
boost::fibers::mutex mtx;
bool data_ready = false;
void process_data();
void wait_for_data_to_process() {
    {
        std::unique_lock< boost::fibers::mutex > lk( mtx);
        while ( ! data_ready) {
            cond.wait( lk);
        }
    }   // release lk
    process_data();
}

Обратите внимание, что<lk>передается.<condition_variable::wait()>:<wait()>атомарно добавит волокно к набору волокон, ожидающих переменную состояния, и разблокирует.<mutex>. После того, как волокно пробуждается,<mutex>снова запирается перед призывом<wait()>. Это позволяет другим волокнам получать<mutex>для обновления общих данных и гарантирует, что данные, связанные с состоянием, правильно синхронизированы.

wait_for_data_to_process() could equivalently be written:

void wait_for_data_to_process() {
    {
        std::unique_lock< boost::fibers::mutex > lk( mtx);
        // make condition_variable::wait() perform the loop
        cond.wait( lk, [](){ return data_ready; });
    }   // release lk
    process_data();
}

Между тем, другое волокно устанавливает<data_ready>на<true>, а затем вызывает либо<condition_variable::notify_one()>, либо<condition_variable::notify_all()>на<condition_variable><cond>, чтобы разбудить одно ожидающее волокно или все ожидающие волокна соответственно.

void retrieve_data();
void prepare_data();
void prepare_data_for_processing() {
    retrieve_data();
    prepare_data();
    {
        std::unique_lock< boost::fibers::mutex > lk( mtx);
        data_ready = true;
    }
    cond.notify_one();
}

Обратите внимание, что тот же<mutex>заблокирован до обновления общих данных, но<mutex>не должен быть заблокирован по вызову<condition_variable::notify_one()>.

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

Boost.Fiber provides both condition_variable and condition_variable_any. boost::fibers::condition_variable can only wait on std::unique_lock< boost::fibers::mutex > while boost::fibers::condition_variable_any can wait on user-defined lock types.

No Spurious Wakeups

Ни<condition_variable>, ни<condition_variable_any>не подлежат ложному пробуждению:<condition_variable::wait()>может проснуться только тогда, когда<condition_variable::notify_one()>или<condition_variable::notify_all()>называется. В этом случае следует использовать один из<wait(lock,predicate)>перегрузок.

Рассмотрим набор потребительских волокон, обрабатывающих предметы из<std::queue>. Очередь постоянно заполнена набором волокон производителя.

Потребительские волокна могут разумно ждать<condition_variable>, пока очередь остается<empty()>.

Поскольку волокна-производители могут<push()>очередей в очередях, они называют<condition_variable::notify_all()>, а не<condition_variable::notify_one()>.

Но данное потребительское волокно вполне может проснуться от<condition_variable::wait()>и найти очередь<empty()>, потому что другие потребительские волокна, возможно, уже обработали все отложенные предметы.

(См. такжеложное пробуждение).

Enumeration cv_status

Операция ожидания может вернуться из-за тайм-аута или нет.

enum class cv_status {
    no_timeout,
    timeout
};
no_timeout

Effects:

Условная переменная пробуждалась с<notify_one>или<notify_all>.

timeout

Effects:

Переменная состояния пробуждалась тайм-аутом.

Class condition_variable_any

#include <boost/fiber/condition_variable.hpp>
namespace boost {
namespace fibers {
class condition_variable_any {
public:
    condition_variable_any();
    ~condition_variable_any();
    condition_variable_any( condition_variable_any const&) = delete;
    condition_variable_any & operator=( condition_variable_any const&) = delete;
    void notify_one() noexcept;
    void notify_all() noexcept;
    template< typename LockType >
    void wait( LockType &);
    template< typename LockType, typename Pred >
    void wait( LockType &, Pred);
    template< typename LockType, typename Clock, typename Duration >
    cv_status wait_until( LockType &,
                          std::chrono::time_point< Clock, Duration > const&);
    template< typename LockType, typename Clock, typename Duration, typename Pred >
    bool wait_until( LockType &,
                     std::chrono::time_point< Clock, Duration > const&,
                     Pred);
    template< typename LockType, typename Rep, typename Period >
    cv_status wait_for( LockType &,
                        std::chrono::duration< Rep, Period > const&);
    template< typename LockType, typename Rep, typename Period, typename Pred >
    bool wait_for( LockType &,
                   std::chrono::duration< Rep, Period > const&,
                   Pred);
};
}}
Constructor
condition_variable_any()

Effects:

Создает объект.

Throws:

Ничего.

Destructor
~condition_variable_any()

Precondition:

Все волокна, ожидающие<*this>, были уведомлены звонком<notify_one>или<notify_all>(хотя соответствующие звонки на<wait>,<wait_for>или<wait_until>не обязательно возвращались).

Effects:

Уничтожает объект.

Member function notify_one()

void notify_one() noexcept;

Effects:

Если какие-либо волокна в настоящее времязаблокированы, ожидая<*this>в вызове<wait>,<wait_for>или<wait_until>, разблокирует одно из этих волокон.

Throws:

Ничего.

Note:

Это произвольно, что ожидание волокна возобновляется.

Member function notify_all()

void notify_all() noexcept;

Effects:

Если какие-либо волокна в настоящее времязаблокированы, ожидая<*this>в вызове<wait>,<wait_for>или<wait_until>, разблокирует все эти волокна.

Throws:

Ничего.

Note:

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

Templated member function wait()

template< typename LockType >
    void wait( LockType & lk);
template< typename LockType, typename Pred >
void wait( LockType & lk, Pred pred);

Precondition:

<lk>запирается текущим волокном, и либо никакое другое волокно в настоящее время не ожидает<*this>, либо выполнение.<mutex()>Функция элемента на<lk>объектах, подаваемых в вызовах на<wait>во всех волокнах, в настоящее время ожидающих на<*this>, возвращала бы то же значение, что и<lk->mutex()>для этого вызова на<wait>.

Effects:

Атомно вызывает<lk.unlock()>и блокирует текущее волокно. При звонке<this->notify_one()>или<this->notify_all()>волокно разблокируется. Когда волокно разблокировано (по какой-либо причине), замок вновь приобретается путем вызова<lk.lock()>до того, как призыв к<wait>вернется. Замок также приобретается путем вызова<lk.lock()>, если функция выходит за исключением. Функция члена, принимающая<pred>, является сокращением для:

while ( ! pred() ) {
    wait( lk);
}

Postcondition:

<lk>запирается текущим волокном.

Throws:

<fiber_error>Если произошла ошибка.

Note:

Предварительное условие немного плотное. Он просто утверждает, что все волокна, одновременно вызывающие<wait>на<*this>, должны ждать на<lk>объектах, управляющихтем же<mutex>. В любом вызове<condition_variable_any::wait()>участвуют три различных объекта: сам<condition_variable_any>,<mutex>координирующий доступ между волокнами и локальным объектом блокировки (например,<std::unique_lock>). В общем, можно разделить продолжительность жизни данного случая<condition_variable_any>на периоды с одним или несколькими волокнами, ожидающими его, разделенными периодами, когда волокна не ждут его. Когда на этом<condition_variable_any>ожидает более одного волокна, все должны пройти блокировки объектов, ссылаясь натот же<mutex>экземпляр.

Templated member function wait_until()

template< typename LockType, typename Clock, typename Duration >
cv_status wait_until( LockType & lk,
                      std::chrono::time_point< Clock, Duration > const& abs_time);
template< typename LockType, typename Clock, typename Duration, typename Pred >
bool wait_until( LockType & lk,
                 std::chrono::time_point< Clock, Duration > const& abs_time,
                 Pred pred);

Precondition:

<lk>запирается текущим волокном, и либо никакое другое волокно в настоящее время не ожидает<*this>, либо выполнение функции члена<mutex()>на объектах<lk>, подаваемых в вызовах на<wait>,<wait_for>или<wait_until>во всех волокнах, ожидающих в настоящее время на<*this>, возвращает то же значение, что и<lk.mutex()>для этого вызова на<wait_until>.

Effects:

Атомно вызывает<lk.unlock()>и блокирует текущее волокно. Волокно разблокируется, когда оно будет уведомлено вызовом<this->notify_one()>или<this->notify_all()>, когда системное время будет равно или позже указанного<abs_time>. Когда волокно разблокировано (по какой-либо причине), замок вновь приобретается, призывая<lk.lock()>до того, как призыв к<wait_until>вернется. Замок также приобретается путем вызова<lk.lock()>, если функция выходит за исключением. Функция члена, принимающего<pred>, является сокращением для:

while ( ! pred() ) {
    if ( cv_status::timeout == wait_until( lk, abs_time) )
        return pred();
}
return true;

То есть, даже если<wait_until()>раз нет, он все равно может вернуться<true>, если<pred()>вернется<true>в то время.

Postcondition:

<lk>запирается текущим волокном.

Throws:

<fiber_error>при возникновении ошибки или исключений, связанных с тайм-аутом.

Returns:

Перегрузка без<pred>возвращается<cv_status::no_timeout>при пробуждении<notify_one()>или<notify_all()>, или<cv_status::timeout>при пробуждении, потому что системное время прошло<abs_time>.

Returns:

Перегрузка, принимающая<pred>, возвращается<false>, если звонок возвращается, потому что было достигнуто время, указанное<abs_time>, и предикат возвращается<false>,<true>в противном случае.

Note:

См.Примечаниедля<condition_variable_any::wait()>.

Templated member function wait_for()

template< typename LockType, typename Rep, typename Period >
cv_status wait_for( LockType & lk,
                    std::chrono::duration< Rep, Period > const& rel_time);
template< typename LockType, typename Rep, typename Period, typename Pred >
bool wait_for( LockType & lk,
               std::chrono::duration< Rep, Period > const& rel_time,
               Pred pred);

Precondition:

<lk>блокируется текущим волокном, и либо никакое другое волокно в настоящее время не ожидает<*this>, либо выполнение функции члена<mutex()>на объектах<lk>, подаваемых в вызовах<wait>,<wait_for>или<wait_until>во всех волокнах, ожидающих в настоящее время<*this>, вернуло бы то же значение, что и<lk.mutex()>для этого вызова<wait_for>.

Effects:

Атомно вызывает<lk.unlock()>и блокирует текущее волокно. Волокно разблокируется при уведомлении по телефону<this->notify_one()>или<this->notify_all()>, когда интервал времени, равный или превышающий указанный<rel_time>, истек. Когда волокно разблокировано (по какой-либо причине), замок вновь приобретается путем вызова<lk.lock()>до того, как призыв к<wait>вернется. Замок также приобретается путем вызова<lk.lock()>, если функция выходит за исключением. Функция<wait_for()>, принимающая<pred>, является сокращением для:

while ( ! pred() ) {
    if ( cv_status::timeout == wait_for( lk, rel_time) ) {
        return pred();
    }
}
return true;

(За исключением того, что<rel_time>корректируется для каждой итерации). Но если бы он был в состоянии<wait_for()>, то он мог бы вернуться<true>, если бы<pred()>вернулся<true>в то время.

Postcondition:

<lk>запирается текущим волокном.

Throws:

<fiber_error>при возникновении ошибки или исключений, связанных с тайм-аутом.

Returns:

Перегрузка без<pred>возвращается<cv_status::no_timeout>, если она пробудилась<notify_one()>или<notify_all()>, или<cv_status::timeout>, если она пробудилась, потому что по крайней мере<rel_time>прошло.

Returns:

Перегрузка, принимающая<pred>, возвращается<false>, если звонок возвращается, потому что, по крайней мере,<rel_time>истек, а предикат возвращается<false>,<true>в противном случае.

Note:

См.Примечаниедля<condition_variable_any::wait()>.

Class condition_variable

#include <boost/fiber/condition_variable.hpp>
namespace boost {
namespace fibers {
class condition_variable {
public:
    condition_variable();
    ~condition_variable();
    condition_variable( condition_variable const&) = delete;
    condition_variable & operator=( condition_variable const&) = delete;
    void notify_one() noexcept;
    void notify_all() noexcept;
    void wait( std::unique_lock< mutex > &);
    template< typename Pred >
    void wait( std::unique_lock< mutex > &, Pred);
    template< typename Clock, typename Duration >
    cv_status wait_until( std::unique_lock< mutex > &,
                          std::chrono::time_point< Clock, Duration > const&);
    template< typename Clock, typename Duration, typename Pred >
    bool wait_until( std::unique_lock< mutex > &,
                     std::chrono::time_point< Clock, Duration > const&,
                     Pred);
    template< typename Rep, typename Period >
    cv_status wait_for( std::unique_lock< mutex > &,
                        std::chrono::duration< Rep, Period > const&);
    template< typename Rep, typename Period, typename Pred >
    bool wait_for( std::unique_lock< mutex > &,
                   std::chrono::duration< Rep, Period > const&,
                   Pred);
};
}}
Constructor
condition_variable()

Effects:

Создает объект.

Throws:

Ничего.

Destructor
~condition_variable()

Precondition:

Все волокна, ожидающие<*this>, были уведомлены звонком<notify_one>или<notify_all>(хотя соответствующие звонки на<wait>,<wait_for>или<wait_until>не обязательно возвращались).

Effects:

Уничтожает объект.

Member function notify_one()

void notify_one() noexcept;

Effects:

Если какие-либо волокна в настоящее времязаблокированы, ожидая<*this>в вызове<wait>,<wait_for>или<wait_until>, разблокирует одно из этих волокон.

Throws:

Ничего.

Note:

Это произвольно, что ожидание волокна возобновляется.

Member function notify_all()

void notify_all() noexcept;

Effects:

Если какие-либо волокна в настоящее времязаблокированы, ожидая<*this>в вызове<wait>,<wait_for>или<wait_until>, разблокирует все эти волокна.

Throws:

Ничего.

Note:

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

Templated member function wait()

void wait( std::unique_lock< mutex > & lk);
template< typename Pred >
void wait( std::unique_lock< mutex > & lk, Pred pred);

Precondition:

<lk>запирается текущим волокном, и либо никакое другое волокно в настоящее время не ожидает<*this>, либо выполнение.<mutex()>Функция элемента на<lk>объектах, подаваемых в вызовах на<wait>во всех волокнах, в настоящее время ожидающих на<*this>, возвращала бы то же значение, что и<lk->mutex()>для этого вызова на<wait>.

Effects:

Атомно вызывает<lk.unlock()>и блокирует текущее волокно. При звонке<this->notify_one()>или<this->notify_all()>волокно разблокируется. Когда волокно разблокировано (по какой-либо причине), замок вновь приобретается путем вызова<lk.lock()>до того, как призыв к<wait>вернется. Замок также приобретается путем вызова<lk.lock()>, если функция выходит за исключением. Функция члена, принимающая<pred>, является сокращением для:

while ( ! pred() ) {
    wait( lk);
}

Postcondition:

<lk>запирается текущим волокном.

Throws:

<fiber_error>Если произошла ошибка.

Note:

Предварительное условие немного плотное. Он просто утверждает, что все волокна, одновременно вызывающие<wait>на<*this>, должны ждать на<lk>объектах, управляющихтем же<mutex>. В любом вызове<condition_variable::wait()>участвуют три различных объекта: сам<condition_variable>,<mutex>координирующий доступ между волокнами и локальным объектом блокировки (например,<std::unique_lock>). В общем, можно разделить продолжительность жизни данного экземпляра<condition_variable>на периоды с одним или несколькими волокнами, ожидающими его, разделенными периодами, когда волокна не ждут его. Когда этого<condition_variable>ожидает более одного волокна, все должны пройти блокировку объектов, ссылаясь натот же<mutex>экземпляр.

Templated member function wait_until()

template< typename Clock, typename Duration >
cv_status wait_until( std::unique_lock< mutex > & lk,
                      std::chrono::time_point< Clock, Duration > const& abs_time);
template< typename Clock, typename Duration, typename Pred >
bool wait_until( std::unique_lock< mutex > & lk,
                 std::chrono::time_point< Clock, Duration > const& abs_time,
                 Pred pred);

Precondition:

<lk>запирается текущим волокном, и либо никакое другое волокно в настоящее время не ожидает<*this>, либо выполнение функции члена<mutex()>на объектах<lk>, подаваемых в вызовах на<wait>,<wait_for>или<wait_until>во всех волокнах, ожидающих в настоящее время на<*this>, возвращает то же значение, что и<lk.mutex()>для этого вызова на<wait_until>.

Effects:

Атомно вызывает<lk.unlock()>и блокирует текущее волокно. Волокно разблокируется, когда оно будет уведомлено вызовом<this->notify_one()>или<this->notify_all()>, когда системное время будет равно или позже указанного<abs_time>. Когда волокно разблокировано (по какой-либо причине), замок вновь приобретается, призывая<lk.lock()>до того, как призыв к<wait_until>вернется. Замок также приобретается путем вызова<lk.lock()>, если функция выходит за исключением. Функция члена, принимающего<pred>, является сокращением для:

while ( ! pred() ) {
    if ( cv_status::timeout == wait_until( lk, abs_time) )
        return pred();
}
return true;

То есть, даже если<wait_until()>раз нет, он все равно может вернуться<true>, если<pred()>вернется<true>в то время.

Postcondition:

<lk>запирается текущим волокном.

Throws:

<fiber_error>при возникновении ошибки или исключений, связанных с тайм-аутом.

Returns:

Перегрузка без<pred>возвращается<cv_status::no_timeout>при пробуждении<notify_one()>или<notify_all()>, или<cv_status::timeout>при пробуждении, потому что системное время прошло<abs_time>.

Returns:

Перегрузка, принимающая<pred>, возвращается<false>, если звонок возвращается, потому что было достигнуто время, указанное<abs_time>, и предикат возвращается<false>,<true>в противном случае.

Note:

См.Примечаниедля<condition_variable::wait()>.

Templated member function wait_for()

template< typename Rep, typename Period >
cv_status wait_for( std::unique_lock< mutex > & lk,
                    std::chrono::duration< Rep, Period > const& rel_time);
template< typename Rep, typename Period, typename Pred >
bool wait_for( std::unique_lock< mutex > & lk,
               std::chrono::duration< Rep, Period > const& rel_time,
               Pred pred);

Precondition:

<lk>блокируется текущим волокном, и либо никакое другое волокно в настоящее время не ожидает<*this>, либо выполнение функции члена<mutex()>на объектах<lk>, подаваемых в вызовах<wait>,<wait_for>или<wait_until>во всех волокнах, ожидающих в настоящее время<*this>, вернуло бы то же значение, что и<lk.mutex()>для этого вызова<wait_for>.

Effects:

Атомно вызывает<lk.unlock()>и блокирует текущее волокно. Волокно разблокируется при уведомлении по телефону<this->notify_one()>или<this->notify_all()>, когда интервал времени, равный или превышающий указанный<rel_time>, истек. Когда волокно разблокировано (по какой-либо причине), замок вновь приобретается путем вызова<lk.lock()>до того, как призыв к<wait>вернется. Замок также приобретается путем вызова<lk.lock()>, если функция выходит за исключением. Функция<wait_for()>, принимающая<pred>, является сокращением для:

while ( ! pred() ) {
    if ( cv_status::timeout == wait_for( lk, rel_time) ) {
        return pred();
    }
}
return true;

(За исключением того, что<rel_time>корректируется для каждой итерации). Но если бы он был в состоянии<wait_for()>, то он мог бы вернуться<true>, если бы<pred()>вернулся<true>в то время.

Postcondition:

<lk>запирается текущим волокном.

Throws:

<fiber_error>при возникновении ошибки или исключений, связанных с тайм-аутом.

Returns:

Перегрузка без<pred>возвращается<cv_status::no_timeout>, если она пробудилась<notify_one()>или<notify_all()>, или<cv_status::timeout>, если она пробудилась, потому что по крайней мере<rel_time>прошло.

Returns:

Перегрузка, принимающая<pred>, возвращается<false>, если звонок возвращается, потому что, по крайней мере,<rel_time>истек, а предикат возвращается<false>,<true>в противном случае.

Note:

См.Примечаниедля<condition_variable::wait()>.


PrevUpHomeNext

Статья Condition Variables раздела Chapter 1. Fiber Synchronization может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Synchronization ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-05 00:04:10/0.0098001956939697/0