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

Synchronized Data Structures

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 35. Thread 4.7.1

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
[Warning] Warning

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

[Note] Note

Этот учебник является адаптацией статьи Энтони Уильямса «Использование правильного мутекса с синхронизированными ценностями» к библиотеке Boost.

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

std::mutex m1;
int value1;
std::mutex m2;
int value2;
int readValue1()
{
  boost::lock_guard<boost::mutex> lk(m1);
  return value1;
}
int readValue2()
{
  boost::lock_guard<boost::mutex> lk(m1); // oops: wrong mutex
  return value2;
}

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

Использование синхронизированного значения решает обе эти проблемы - mutex тесно связан со значением, поэтому вы не можете получить к нему доступ без замка, и все же семантика доступа по-прежнему проста. Для простых доступов синхронизированное значение ведет себя как указатель на Т; например:

boost::synchronized_value<std::string> value3;
std::string readValue3()
{
  return *value3;
}
void setValue3(std::string const& newVal)
{
  *value3=newVal;
}
void appendToValue3(std::string const& extra)
{
  value3->append(extra);
}

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

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

Вызывая синхронизацию (), вы получаете объект strict_lock_ptr, который содержит блокировку на mutex, защищающую данные, и который может быть использован для доступа к защищенным данным. Замок удерживается до тех пор, пока объект strict_lock_ptr не будет уничтожен, поэтому вы можете безопасно выполнять многочастные операции. Объект strict_lock_ptr также действует как указатель на T, как и синхронизированное значение, но на этот раз замок уже удерживается. Например, следующая функция добавляет следующий слэш к пути, удерживаемому в синхронизированном значении. Использование объекта strict_lock_ptr гарантирует, что строка не изменилась между запросом и обновлением.

void addTrailingSlashIfMissing(boost::synchronized_value<std::string> & path)
{
  boost::strict_lock_ptr<std::string> u=path.synchronize();
  if(u->empty() || (*u->rbegin()!='/'))
  {
    *u+='/';
  }
}

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

Одним из способов защиты доступа к двум объектам синхронизированного значения является построение строгого _lock_ptr для каждого объекта и использование их для доступа к соответствующим защищенным значениям; например:

synchronized_value<std::queue<MessageType> > q1,q2;
void transferMessage()
{
  strict_lock_ptr<std::queue<MessageType> > u1 = q1.synchronize();
  strict_lock_ptr<std::queue<MessageType> > u2 = q2.synchronize();
  if(!u1->empty())
  {
    u2->push_back(u1->front());
    u1->pop_front();
  }
}

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

Чтобы иметь возможность использовать алгоритмы блокировки без замков, нам нужно использовать уникальный_lock_ptr, который является Lockable.

synchronized_value<std::queue<MessageType> > q1,q2;
void transferMessage()
{
  unique_lock_ptr<std::queue<MessageType> > u1 = q1.unique_synchronize(boost::defer_lock);
  unique_lock_ptr<std::queue<MessageType> > u2 = q2.unique_synchronize(boost::defer_lock);
  boost::lock(u1,u2); // dead-lock free algorithm
  if(!u1->empty())
  {
    u2->push_back(u1->front());
    u1->pop_front();
  }
}

В то время как предыдущий заботится о тупике, доступ к синхронизированному значению через уникальный_lock_ptr требует блокировки, которая не навязывается интерфейсом. Альтернативой компиляторам, обеспечивающим стандартную библиотеку, поддерживающую подвижный std::tuple, является использование функции свободной синхронизации, которая блокирует все мутексы, связанные с синхронизированными значениями, и возвращает tuple os strict_lock_ptr.

synchronized_value<std::queue<MessageType> > q1,q2;
void transferMessage()
{
  auto lks = synchronize(u1,u2); // dead-lock free algorithm
  if(!std::get<1>(lks)->empty())
  {
    std::get<2>(lks)->push_back(u1->front());
    std::get<1>(lks)->pop_front();
  }
}

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

#include <boost/thread/synchronized_value.hpp>
namespace boost
{
  template<typename T, typename Lockable = mutex>
  class synchronized_value;
  // Specialized swap algorithm
  template <typename T, typename L>
  void swap(synchronized_value<T,L> & lhs, synchronized_value<T,L> & rhs);
  template <typename T, typename L>
  void swap(synchronized_value<T,L> & lhs, T & rhs);
  template <typename T, typename L>
  void swap(T & lhs, synchronized_value<T,L> & rhs);
  // Hash support
  template<typename T, typename L>
  struct hash<synchronized_value<T,L> >;
  // Comparison
  template <typename T, typename L>
  bool operator==(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs)
  template <typename T, typename L>
  bool operator!=(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs)
  template <typename T, typename L>
  bool operator<(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs)
  template <typename T, typename L>
  bool operator<=(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs)
  template <typename T, typename L>
  bool operator>(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs)
  template <typename T, typename L>
  bool operator>=(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs)
  // Comparison with T
  template <typename T, typename L>
  bool operator==(T const& lhs, synchronized_value<T,L> const&rhs);
  template <typename T, typename L>
  bool operator!=(T const& lhs, synchronized_value<T,L> const&rhs);
  template <typename T, typename L>
  bool operator<(T const& lhs, synchronized_value<T,L> const&rhs);
  template <typename T, typename L>
  bool operator<=(T const& lhs, synchronized_value<T,L> const&rhs);
  template <typename T, typename L>
  bool operator>(T const& lhs, synchronized_value<T,L> const&rhs);
  template <typename T, typename L>
  bool operator>=(T const& lhs, synchronized_value<T,L> const&rhs);
  template <typename T, typename L>
  bool operator==(synchronized_value<T,L> const& lhs, T const& rhs);
  template <typename T, typename L>
  bool operator!=(synchronized_value<T,L> const& lhs, T const& rhs);
  template <typename T, typename L>
  bool operator<(synchronized_value<T,L> const& lhs, T const& rhs);
  template <typename T, typename L>
  bool operator<=(synchronized_value<T,L> const& lhs, T const& rhs);
  template <typename T, typename L>
  bool operator>(synchronized_value<T,L> const& lhs, T const& rhs);
  template <typename T, typename L>
  bool operator>=(synchronized_value<T,L> const& lhs, T const& rhs);
#if ! defined(BOOST_THREAD_NO_SYNCHRONIZE)
  template <typename ...SV>
  std::tuple<typename synchronized_value_strict_lock_ptr<SV>::type ...> synchronize(SV& ...sv);
#endif
}
#include <boost/thread/synchronized_value.hpp>
namespace boost
{
  template<typename T, typename Lockable = mutex>
  class synchronized_value
  {
  public:
    typedef T value_type;
    typedef Lockable mutex_type;
    synchronized_value() noexcept(is_nothrow_default_constructible<T>::value);
    synchronized_value(T const& other) noexcept(is_nothrow_copy_constructible<T>::value);
    synchronized_value(T&& other) noexcept(is_nothrow_move_constructible<T>::value);
    synchronized_value(synchronized_value const& rhs);
    synchronized_value(synchronized_value&& other);
    // mutation
    synchronized_value& operator=(synchronized_value const& rhs);
    synchronized_value& operator=(value_type const& val);
    void swap(synchronized_value & rhs);
    void swap(value_type & rhs);
    //observers
    T get() const;
  #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS)
    explicit operator T() const;
  #endif
    strict_lock_ptr<T,Lockable> operator->();
    const_strict_lock_ptr<T,Lockable> operator->() const;
    strict_lock_ptr<T,Lockable> synchronize();
    const_strict_lock_ptr<T,Lockable> synchronize() const;
    deref_value operator*();;
    const_deref_value operator*() const;
  private:
    T value_; // for exposition only
    mutable mutex_type mtx_;  // for exposition only
  };
}

Requires:

<Lockable>является<Lockable>.

synchronized_value() noexcept(is_nothrow_default_constructible<T>::value);

Requires:

<T>является<DefaultConstructible>.

Effects:

Дефолт конструирует скрытое значение_тип

Throws:

Исключение составляют<value_type()>.

synchronized_value(T const& other) noexcept(is_nothrow_copy_constructible<T>::value);

Requires:

<T><CopyConstructible>.

Effects:

Копия конструирует скрытое значение_тип с помощью параметра<other>

Throws:

Исключение составляет<value_type(other)>.

synchronized_value(synchronized_value const& rhs);

Requires:

<T>является<DefaultConstructible>и<Assignable>.

Effects:

Назначает значение на область, защищенную мутексом rhs. Мутекс не копируется.

Throws:

Всякое исключение<value_type()>или<value_type&operator=(value_type&)>или<mtx_.lock()>.

synchronized_value(T&& other) noexcept(is_nothrow_move_constructible<T>::value);

Requires:

<T>является<MoveConstructible>.

Effects:

Move создает скрытое значение_type

Throws:

Исключение составляет<value_type(value_type&&)>.

synchronized_value(synchronized_value&& other);

Requires:

<T>является<MoveConstructible>.

Effects:

Move создает скрытое значение_type

Throws:

Всякое исключение<value_type(value_type&&)>или<mtx_.lock()>.

synchronized_value& operator=(synchronized_value const& rhs);

Requires:

<T>является<Assignable>.

Effects:

Копиирует базовое значение в области, защищенной двумя мутексами. Мутекс не копируется. Замки приобретаются, избегая тупика. Например, нет проблем, если одна нить присваивает<a= b>, а другая присваивает<b= a>.

Return:

<*this>

Throws:

Любое исключение<value_type&operator(value_type const&)>или<mtx_.lock()>.

synchronized_value& operator=(value_type const& val);

Requires:

<T>является<Assignable>.

Effects:

Копиирует значение в области, защищенной mutex.

Return:

<*this>

Throws:

Любое исключение<value_type&operator(value_type const&)>или<mtx_.lock()>.

T get() const;

Requires:

<T><CopyConstructible>.

Return:

<Acopy ofthe protectedvalue obtainedon ascope protectedby themutex.>

Throws:

Всякое исключение<value_type(value_type const&)>или<mtx_.lock()>.

#if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS)
  explicit operator T() const;
#endif

Requires:

<T><CopyConstructible>.

Return:

<Acopy ofthe protectedvalue obtainedon ascope protectedby themutex.>

Throws:

Всякое исключение<value_type(value_type const&)>или<mtx_.lock()>.

void swap(synchronized_value & rhs);

Requires:

<T>является<Assignable>.

Effects:

Обменяет данные на область, защищенную обоими mutex. Оба мутекса приобретаются, чтобы избежать тупика. Мутексы не меняются.

Throws:

Всякое исключение<swap(value_,rhs.value)>или<mtx_.lock()>или<rhs_.mtx_.lock()>.

void swap(value_type & rhs);

Requires:

<T>—<Swapable>.

Effects:

Обменяет данные на область, защищенную обоими mutex. Оба мутекса приобретаются, чтобы избежать тупика. Мутексы не меняются.

Throws:

Всякое исключение<swap(value_,rhs)>или<mtx_.lock()>.

strict_lock_ptr<T,Lockable> operator->();

По существу, вызов метода<obj->foo(x,y,z)>вызывает метод<foo(x,y,z)>внутри критической секции, столь же долгоживущей, как и сам вызов.

Return:

<Astrict_lock_ptr<>.>

Throws:

Ничего.

const_strict_lock_ptr<T,Lockable> operator->() const;

Если<synchronized_value>объект, участвующий в этом, является конст-квалифицированным, то вы сможете назвать методы конст только через<operator->>. Так, например,<vec->push_back("xyz")>не сработает, если<vec>будут квалифицированы. Механизм блокировки основан на предположении, что методы const не изменяют свои базовые данные.

Return:

<Aconst_strict_lock_ptr <>.>

Throws:

Ничего.

strict_lock_ptr<T,Lockable> synchronize();

Завод синхронизации () облегчает блокировку прицела. Как уже говорилось,<operator->>может блокировать только длительность вызова, поэтому его недостаточно для сложных операций. С<synchronize()>вы можете заблокировать объект в прицеле и получить прямой доступ к объекту внутри этого прицела.

Пример:

void fun(synchronized_value<vector<int>> & vec) {
  auto vec2=vec.synchronize();
  vec2.push_back(42);
  assert(vec2.back() == 42);
}

Return:

<Astrict_lock_ptr <>.>

Throws:

Ничего.

const_strict_lock_ptr<T,Lockable> synchronize() const;

Return:

<Aconst_strict_lock_ptr <>.>

Throws:

Ничего.

deref_value operator*();;

Return:

<Aan instanceof aclass thatlocks themutex onconstruction andunlocks iton destructionand providesimplicit conversionto areference tothe protectedvalue.>

Throws:

Ничего.

const_deref_value operator*() const;

Return:

<Aan instanceof aclass thatlocks themutex onconstruction andunlocks iton destructionand providesimplicit conversionto aconstant referenceto theprotected value.>

Throws:

Ничего.

#include <boost/thread/synchronized_value.hpp>
namespace boost
{
#if ! defined(BOOST_THREAD_NO_SYNCHRONIZE)
  template <typename ...SV>
  std::tuple<typename synchronized_value_strict_lock_ptr<SV>::type ...> synchronize(SV& ...sv);
#endif
}
[Warning] Warning

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

[Note] Note

Эти функции основаны на предложенииN3533 - C++ Concurrent QueuesC++1y от Лоуренса Кроула и Криса Майсена иC++ Concurrency in Actionот Энтони Уильямса.

Очереди обеспечивают механизм передачи данных между компонентами системы.

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

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

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

Параллельные очереди по своей сути копируют / перемещают семантику для операции обработки данных. Интерфейсы возврата ссылок запрещены, поскольку множественный доступ к этим ссылкам не может быть безвредным.

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

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

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

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

Для достижения этого сигнала нить может закрыть очередь. После закрытия никакие новые элементы не могут быть перенесены в очередь. Операции нажатия на закрытую очередь будут либо возвращать queue_op_status::closed (когда у них есть тип возврата queue_op_status), либо устанавливать закрытый параметр, если он имеет одну, либо бросать sync_queue::closed (когда у них нет). Элементы, уже находящиеся в очереди, могут быть сняты. Когда очередь пуста и закрыта, операции вытягивания либо возвращают queue_op_status::closed (когда они имеют статус возврата), устанавливают закрытый параметр, если он имеет один, либо бросают sync_queue::closed (когда они этого не делают).

Все функции определяются так, как если бы в дополнение к спецификации Throw мы имели следующее:

Throws:

Любое исключение бросается внутренним замком.

Все функции, которые выделяют ресурс, определяются так, как если бы мы имели в дополнение к его конкретной спецификации Бросок следующее:

Throws:

Любое исключение из-за ошибок распределения.

Существенным решением проблемы параллельных очередей является переход к операциям, основанным на стоимости, а не на справочных операциях.

Концепция BasicConcurrentQueue моделирует основные операции одновременной очереди.

Тип<Q>соответствует требованиям BasicConcurrentQueue, если следующие выражения хорошо сформированы и имеют указанную семантику.

  • Q: Value_type
  • Q::ize_type
  • <q.push_back(e);>
  • <q.push_back(rve);>
  • <q.pull_front(lre);>
  • <lre= q.pull_front();>
  • <b= q.empty();>
  • <u= q.size();>

где

  • <q>обозначает величину типа<Q>,
  • <e>обозначает значение типа Q::value_type,
  • <u>обозначает значение типа Q::size_type,
  • <lve>обозначает значение l типа Q::value_type,
  • <rve>обозначает rзначение типа Q::value_type:
  • <qs>обозначает переменную типа<queue_op_status>,

Effects:

Ждет, пока очередь не будет заполнена (для ограниченных очередей), а затем отодвигает<e>в очередь, копируя ее (это может потребовать выделения для неограниченных очередей).

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

<!q.empty()>.

Return type:

<void>.

Throws:

Если очередь была закрыта, то бросает sync_queue_is_closed. Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Effects:

Подождет, пока очередь не будет заполнена (для ограниченных очередей), а затем подтолкнет<e>к очереди, перемещая ее обратно в очередь (это может потребовать выделения для неограниченных очередей).

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

<!q.empty()>.

Return type:

<void>.

Throws:

Если очередь закрыта, выбрасывает sync_queue_is_closed. Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Effects:

Подождет, пока очередь не опустеет, а затем вытянет элемент из очереди<q>и переместит вытянутый элемент в<lve>(это может потребовать выделения для неограниченных очередей).

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

(108).

Return type:

<void>.

Throws:

Любое исключение, вызванное движением<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Requires:

Q::value_type не является конструируемым броском. Это необходимо для обеспечения безопасности исключения.

Effects:

Ждет, пока очередь не опустеет и не закроется. Если очередь пуста и закрыта, то синхронизация_queue_is_закрывается. В противном случае вытащите элемент из очереди<q>и переместите вытянутый элемент.

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

(108).

Return type:

<Q::value_type>.

Return:

Вытянутый элемент.

Throws:

Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Концепт ConcurrentQueue моделирует очередь с операциями без ожидания.

Тип<Q>соответствует требованиям ConcurrentQueue, если является моделью BasicConcurrentQueue и следующие выражения хорошо сформированы и имеют указанную семантику.

  • <s= q.try_push_back(e);>
  • <s= q.try_push_back(rve);>
  • <s= q.try_pull_front(lre);>

где

  • <q>обозначает величину типа<Q>,
  • <e>обозначает значение типа<Q::value_type>,
  • <s>обозначает значение типа<queue_status>,
  • <u>обозначает значение типа<Q::size_type>,
  • <lve>обозначает значение l типа Q::value_type,
  • <rve>обозначает rзначение типа Q::value_type:

Effects:

Если очередь<q>не заполнена и не закрыта, отодвиньте<e>в очередь, копирующую ее.

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией, когда операция проходит успешно.

Return type:

131.

Return:

Если очередь закрыта, возвращается<queue_op_status::closed>,

В случае, если очередь<q>полностью возвращается<queue_op_status::full>,

- в обратном порядке<queue_op_status::success>;

Postcondition:

Если ответят<queue_op_status::success>,<!q.empty()>.

Throws:

Если очередь закрыта, выбрасывает sync_queue_is_closed. Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Effects:

Если очередь<q>не заполнена и не закрыта, нажмите<e>на очередь, перемещая ее.

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Return type:

131.

Return:

Если очередь закрыта, возвращается<queue_op_status::closed>,

В случае, если очередь<q>полностью возвращается<queue_op_status::full>,

- в обратном порядке<queue_op_status::success>;

Postcondition:

Если ответят<queue_op_status::success>,<!q.empty()>.

Throws:

Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Effects:

Если очередь не пуста, вытаскивает элемент из очереди<q>и перемещает вытянутый элемент в<lve>(это может потребовать выделения для неограниченных очередей).

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

(108).

Return type:

<bool>.

Return:

Если в очереди<q>пустой возврат<queue_op_status::empty>,

- в обратном порядке<queue_op_status::success>;

Throws:

Любое исключение, вызванное движением<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Для случаев, когда блокировка для взаимного исключения нежелательна, у нас есть неблокирующие операции. Интерфейс такой же, как и в пробных операциях, но позволяет также возвращать queue_op_status::busy в случае невозможности выполнения операции без блокировки.

Неблокирующие операции предоставляются только для очередей на основе блокировки

  • <s= q.nonblocking_push_back(nb, e);>
  • <s= q.nonblocking_push_back(nb, rve);>
  • <s= q.nonblocking_pull_front(nb, lre);>

где

  • <q>обозначает величину типа<Q>,
  • <e>обозначает значение типа Q::value_type,
  • <s>обозначает значение типа<queue_status>,
  • <lve>обозначает значение l типа Q::value_type,
  • <rve>обозначает rзначение типа Q::value_type:

Effects:

Если очередь<q>не заполнена и не закрыта, отодвиньте<e>в очередь, копирующую ее.

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией, когда операция проходит успешно.

Return type:

131.

Return:

Если операция будет заблокирована, верните queue_op_status::busy,

- в противном случае, если очередь закрыта, возврат<queue_op_status::closed>,

В случае, если очередь<q>полностью возвращается<queue_op_status::full>,

- в обратном порядке<queue_op_status::success>;

Postcondition:

Если ответят<queue_op_status::success>,<!q.empty()>.

Throws:

Если очередь закрыта, выбрасывает sync_queue_is_closed. Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Effects:

Если очередь<q>не заполнена и не закрыта, нажмите<e>на очередь, перемещая ее.

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Return type:

131.

Return:

Если операция будет заблокирована, верните queue_op_status::busy,

- в противном случае, если очередь закрыта, возвращается<queue_op_status::closed>,

В случае, если очередь<q>полностью возвращается<queue_op_status::full>,

- в обратном порядке<queue_op_status::success>;

Postcondition:

Если ответят<queue_op_status::success>,<!q.empty()>.

Throws:

Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Effects:

Если очередь не пуста, вытаскивает элемент из очереди<q>и перемещает вытянутый элемент в<lve>(это может потребовать выделения для неограниченных очередей).

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

(108).

Return type:

<bool>.

Return:

Если операция будет заблокирована, верните queue_op_status::busy,

- в противном случае, если очередь<q>является пустым возвратом<queue_op_status::empty>,

- в обратном порядке<queue_op_status::success>;

Throws:

Любое исключение, вызванное движением<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Ограниченные очереди добавляют следующие действительные выражения:

  • <Qq(u);>
  • <b= q.full();>
  • <u= q.capacity();>

где

  • <q>обозначает величину типа<Q>,
  • <b>обозначает значение типа<bool>,
  • <u>обозначает значение типа<Q::size_type>,

Return type:

<bool>.

Return:

Возвращение<true>Очередь полная.

Remark:

Не все очереди будут иметь полное состояние, и они всегда будут ложными, если функция будет предоставлена.

Return type:

<Q::size_type>.

Return:

Верните вместимость очереди.

Закрытые очереди добавляют следующие действительные выражения:

  • <q.close();>
  • <b= q.closed();>
  • <s= q.wait_push_back(e);>
  • <s= q.wait_push_back(rve);>
  • <s= q.wait_pull_front(lre);>

Effects:

Закройте очередь.

Return type:

<bool>.

Return:

Возвращение<true>Очередь закрыта.

Effects:

Ждет, пока очередь не будет заполнена (для ограниченных очередей), а затем отодвигает<e>в очередь, копируя ее (это может потребовать выделения для неограниченных очередей).

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

<!q.empty()>.

Return type:

131.

Return:

- если очередь закрыта<queue_op_status::closed>,

В противном случае, возврат<queue_op_status::success>, если не будет брошено исключение.

Throws:

Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Effects:

Подождет, пока очередь не будет заполнена (для ограниченных очередей), а затем подтолкнет<e>к очереди, перемещая ее обратно в очередь (это может потребовать выделения для неограниченных очередей).

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

<!q.empty()>.

Return type:

131.

Return:

- если очередь закрыта<queue_op_status::closed>,

В противном случае, возврат<queue_op_status::success>, если не будет брошено исключение.

.

Throws:

Исключение составляет копия<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

Effects:

Если очередь не пуста и не закрыта, подождите, пока очередь не опустеет, а затем вытяните элемент из очереди<q>и переместите вытянутый элемент в<lve>.

Synchronization:

Предыдущие тягоподобные операции на одном и том же объекте синхронизируются с этой операцией.

Postcondition:

(108).

Return type:

131.

Return:

Если очередь пуста и закрыта, возвращайтесь<queue_op_status::closed>.

В противном случае, возврат<queue_op_status::success>, если не будет брошено исключение.

Throws:

Любое исключение, вызванное движением<e>.

Exception safety:

Если выпадает исключение, то состояние очереди не изменяется.

#include <boost/thread/concurrent_queues/queue_op_status.hpp>
namespace boost
{
   enum class queue_op_status { success = 0, empty, full, closed, busy }
}
#include <boost/thread/concurrent_queues/queue_base.hpp>
namespace boost
{
  template <typename ValueType, class SizeType=std::size_t>
  class queue_base
  {
  public:
    typedef ValueType value_type;
    typedef SizeType size_type;
    // Constructors/Assignment/Destructors
    virtual ~queue_base() {};
    // Observers
    virtual bool empty() const = 0;
    virtual bool full() const = 0;
    virtual size_type size() const = 0;
    virtual bool closed() const = 0;
    // Modifiers
    virtual void close() = 0;
    virtual void push_back(const value_type& x) = 0;
    virtual void push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
    virtual void pull_front(value_type&) = 0;
    virtual value_type pull_front() = 0;
    virtual queue_op_status try_push_back(const value_type& x) = 0;
    virtual queue_op_status try_push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
    virtual queue_op_status try_pull_front(value_type&) = 0;
    virtual queue_op_status nonblocking_push_back(const value_type& x) = 0;
    virtual queue_op_status nonblocking_push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
    virtual queue_op_status nonblocking_pull_front(value_type&) = 0;
    virtual queue_op_status wait_push_back(const value_type& x) = 0;
    virtual queue_op_status wait_push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
    virtual queue_op_status wait_pull_front(value_type& elem) = 0;
  };
}
#include <boost/thread/concurrent_queues/queue_adaptor.hpp>
namespace boost
{
  template <typename Queue>
  class queue_adaptor : public queue_base<typename Queue::value_type, typename Queue::size_type>
  {
  public:
    typedef typename Queue::value_type value_type;
    typedef typename Queue::size_type size_type;
    // Constructors/Assignment/Destructors
    queue_adaptor();
    // Observers
    bool empty() const;
    bool full() const;
    size_type size() const { return queue.size(); }
    bool closed() const;
    // Modifiers
    void close();
    void push_back(const value_type& x);
    void push_back(BOOST_THREAD_RV_REF(value_type) x);
    void pull_front(value_type& x);
    value_type pull_front();
    queue_op_status try_push_back(const value_type& x);
    queue_op_status try_push_back(BOOST_THREAD_RV_REF(value_type) x);
    queue_op_status try_pull_front(value_type& x);
    queue_op_status nonblocking_push_back(const value_type& x);
    queue_op_status nonblocking_push_back(BOOST_THREAD_RV_REF(value_type) x);
    queue_op_status nonblocking_pull_front(value_type& x);
    queue_op_status wait_push_back(const value_type& x);
    queue_op_status wait_push_back(BOOST_THREAD_RV_REF(value_type) x);
    queue_op_status wait_pull_front(value_type& x);
  };
}
#include <boost/thread/concurrent_queues/queue_views.hpp>
namespace boost
{
  template <typename Queue>
  class queue_back_view;
  template <typename Queue>
  class queue_front_view
  template <class T>
  using queue_back = queue_back_view<queue_base<T>>;
  template <class T>
  using queue_front = queue_front_view<queue_base<T>>;
}
template <typename Queue>
class queue_back_view
{
public:
  typedef typename Queue::value_type value_type;
  typedef typename Queue::size_type size_type;
  // Constructors/Assignment/Destructors
  queue_back_view(Queue& q) noexcept;
  // Observers
  bool empty() const;
  bool full() const;
  size_type size() const;
  bool closed() const;
  // Modifiers
  void close();
  void push(const value_type& x);
  void push(BOOST_THREAD_RV_REF(value_type) x);
  queue_op_status try_push(const value_type& x);
  queue_op_status try_push(BOOST_THREAD_RV_REF(value_type) x);
  queue_op_status nonblocking_push(const value_type& x);
  queue_op_status nonblocking_push(BOOST_THREAD_RV_REF(value_type) x);
  queue_op_status wait_push(const value_type& x);
  queue_op_status wait_push(BOOST_THREAD_RV_REF(value_type) x);
};
template <typename Queue>
class queue_front_view
{
public:
  typedef typename Queue::value_type value_type;
  typedef typename Queue::size_type size_type;
  // Constructors/Assignment/Destructors
  queue_front_view(Queue& q) BOOST_NOEXCEPT;
  // Observers
  bool empty() const;
  bool full() const;
  size_type size() const;
  bool closed() const;
  // Modifiers
  void close();
  void pull(value_type& x);
  value_type pull();
  queue_op_status try_pull(value_type& x);
  queue_op_status nonblocking_pull(value_type& x);
  queue_op_status wait_pull(value_type& x);
};
#include <boost/thread/sync_bounded_queue.hpp>
namespace boost
{
  struct sync_queue_is_closed : std::exception {};
  template <typename ValueType>
  class sync_bounded_queue;
  // Stream-like operators
  template <typename ValueType>
  sync_bounded_queue<ValueType>& operator<<(sync_bounded_queue<ValueType>& sbq, ValueType&& elem);
  template <typename ValueType>
  sync_bounded_queue<ValueType>& operator<<(sync_bounded_queue<ValueType>& sbq, ValueType const&elem);
  template <typename ValueType>
  sync_bounded_queue<ValueType>& operator>>(sync_bounded_queue<ValueType>& sbq, ValueType &elem);
}
#include <boost/thread/sync_bounded_queue.hpp>
namespace boost
{
  struct sync_queue_is_closed : std::exception {};
}
#include <boost/thread/sync_bounded_queue.hpp>
namespace boost
{
  template <typename ValueType>
  class sync_bounded_queue
  {
  public:
    typedef ValueType value_type;
    typedef std::size_t size_type;
    sync_bounded_queue(sync_bounded_queue const&) = delete;
    sync_bounded_queue& operator=(sync_bounded_queue const&) = delete;
    explicit sync_bounded_queue(size_type max_elems);
    template <typename Range>
    sync_bounded_queue(size_type max_elems, Range range);
    ~sync_bounded_queue();
    // Observers
    bool empty() const;
    bool full() const;
    size_type capacity() const;
    size_type size() const;
    bool closed() const;
    // Modifiers
    void push_back(const value_type& x);
    void push_back(value_type&& x);
    queue_op_status try_push_back(const value_type& x);
    queue_op_status try_push_back(value_type&&) x);
    queue_op_status nonblocking_push_back(const value_type& x);
    queue_op_status nonblocking_push_back(value_type&& x);
    void pull_front(value_type&);
    value_type pull_front();
    queue_op_status try_pull_front(value_type&);
    queue_op_status nonblocking_pull_front(value_type&);
    void close();
  };
}
explicit sync_bounded_queue(size_type max_elems);

Effects:

Построение синхронизированной очереди с максимальным количеством элементов, заданных<max_elems>.

Throws:

Любое исключение, которое можно бросить из-за недоступности ресурсов.

template <typename Range>
sync_bounded_queue(size_type max_elems, Range range);

Effects:

Конструирует синхронизированную очередь с максимальным количеством элементов, заданных<max_elems>, и отодвигает элементы диапазона.

Throws:

Любое исключение, которое можно бросить из-за недоступности ресурсов.

#include <boost/thread/sync_bounded_queue.hpp>
namespace boost
{
  template <typename ValueType>
  sync_bounded_queue<ValueType>& operator<<(sync_bounded_queue<ValueType>& sbq, ValueType&& elem);
  template <typename ValueType>
  sync_bounded_queue<ValueType>& operator<<(sync_bounded_queue<ValueType>& sbq, ValueType const&elem);
}
#include <boost/thread/sync_bounded_queue.hpp>
namespace boost
{
  template <typename ValueType>
  sync_bounded_queue<ValueType>& operator>>(sync_bounded_queue<ValueType>& sbq, ValueType &elem);
}
#include <boost/thread/sync_queue.hpp>
namespace boost
{
  template <typename ValueType>
  class sync_queue;
  // Stream-like operators
  template <typename ValueType>
  sync_queue<ValueType>& operator<<(sync_queue<ValueType>& sbq, ValueType&& elem);
  template <typename ValueType>
  sync_queue<ValueType>& operator<<(sync_queue<ValueType>& sbq, ValueType const&elem);
  template <typename ValueType>
  sync_queue<ValueType>& operator>>(sync_queue<ValueType>& sbq, ValueType &elem);
}
#include <boost/thread/sync_queue.hpp>
namespace boost
{
  template <typename ValueType, class Container = csbl::devector<ValueType>>
  class sync_queue
  {
  public:
    typedef ValueType value_type;
    typedef Container underlying_queue_type;
    typedef typename Container::size_type size_type;
    sync_queue(sync_queue const&) = delete;
    sync_queue& operator=(sync_queue const&) = delete;
    sync_queue();
    explicit template <typename Range>
    sync_queue(Range range); // Not yet implemented
    ~sync_queue();
    // Observers
    bool empty() const;
    bool full() const;
    size_type size() const;
    bool closed() const;
    // Modifiers
    void push_back(const value_type& x);
    void push_back(value_type&& x);
    queue_op_status try_push_back(const value_type& x);
    queue_op_status try_push_back(value_type&&) x);
    queue_op_status nonblocking_push_back(const value_type& x);
    queue_op_status nonblocking_push_back(value_type&& x);
    void pull_front(value_type&);
    value_type pull_front();
    queue_op_status try_pull_front(value_type&);
    queue_op_status nonblocking_pull_front(value_type&);
    underlying_queue_type underlying_queue() noexcept;
    void close();
  };
}
explicit sync_queue();

Effects:

Построение пустой синхронизации.

Throws:

Любое исключение, которое можно бросить из-за недоступности ресурсов.

bool full() const;

Returns:

Ложная.

underlying_queue_type underlying_queue() noexcept;

Returns:

Перемещает внутреннюю очередь.

#include <boost/thread/sync_queue.hpp>
namespace boost
{
  template <typename ValueType>
  sync_queue<ValueType>& operator<<(sync_queue<ValueType>& sbq, ValueType&& elem);
  template <typename ValueType>
  sync_queue<ValueType>& operator<<(sync_queue<ValueType>& sbq, ValueType const&elem);
}
#include <boost/thread/sync_queue.hpp>
namespace boost
{
  template <typename ValueType>
  sync_queue<ValueType>& operator>>(sync_queue<ValueType>& sbq, ValueType &elem);
}

PrevUpHomeNext

Статья Synchronized Data Structures раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 35. Thread 4.7.1 может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Chapter 35. Thread 4.7.1 ::


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 17:48:07/0.042644023895264/1