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

Synchronization

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

Обработка мутексов в C++Прекрасный учебник. Вам просто нужно заменить std и ting на boost.

Mutex, Lock, Condition Variable Rationaleдобавляет обоснование для проектных решений, принятых для мутексов, замков и переменных условий.

В дополнение к стандартным замкам C++11, Boost. Thread предоставляет другие блокировки и некоторые утилиты, которые помогают пользователю сделать свой код безопасным.

[Note]Note

Этот учебник является адаптацией главы «Соответствие объектно-ориентированного программирования на языке программирования BETA» и статьи Андрея Александреску «Многопоточность и система типов C++» к библиотеке Boost.

Рассмотрим, например, моделирование класса банковских счетов, который поддерживает одновременные депозиты и снятие средств из нескольких мест (возможно, «Привет, мир» многопоточного программирования).

Здесь компонент является моделью концепции<Callable>.

I C++11 (Boost) одновременное выполнение компонента получается с помощью<std::thread>(<boost::thread>):

boost::thread thread1(S);

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

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

class BankAccount;
BankAccount JoesAccount;
void bankAgent()
{
    for (int i =10; i>0; --i) {
        //...
        JoesAccount.Deposit(500);
        //...
    }
}
void Joe() {
    for (int i =10; i>0; --i) {
        //...
        int myPocket = JoesAccount.Withdraw(100);
        std::cout << myPocket << std::endl;
        //...
    }
}
int main() {
    //...
    boost::thread thread1(bankAgent); // start concurrent execution of bankAgent
    boost::thread thread2(Joe); // start concurrent execution of Joe
    thread1.join();
    thread2.join();
    return 0;
}

Время от времени<bankAgent>вносит 500 долларов в<JoesAccount>.<Joe>аналогичным образом снимает 100 долларов со своего счета. Эти слова обозначают, что<bankAgent>и<Joe>исполняются одновременно.

Приведенный выше пример хорошо работает, если компоненты<bankAgent>и<Joe>не имеют доступа<JoesAccount>одновременно. Однако нет никакой гарантии, что этого не произойдет. Мы можем использовать mutex, чтобы гарантировать эксклюзивный доступ к каждому банку.

class BankAccount {
    boost::mutex mtx_;
    int balance_;
public:
    void Deposit(int amount) {
        mtx_.lock();
        balance_ += amount;
        mtx_.unlock();
    }
    void Withdraw(int amount) {
        mtx_.lock();
        balance_ -= amount;
        mtx_.unlock();
    }
    int GetBalance() {
        mtx_.lock();
        int b = balance_;
        mtx_.unlock();
        return b;
    }
};

Выполнение операций<Deposit>и<Withdraw>больше не сможет обеспечить одновременный доступ к балансу.

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

С идиомой RAII мы можем многое упростить, используя расширенные замки. В приведенном ниже коде конструктор охранника блокирует пропущенный объект<mtx_>, а деструктор охраны разблокирует<mtx_>.

class BankAccount {
    boost::mutex mtx_; // explicit mutex declaration 
    int balance_;
public:
    void Deposit(int amount) {
        boost::lock_guard<boost::mutex> guard(mtx_);
        balance_ += amount;
    }
    void Withdraw(int amount) {
        boost::lock_guard<boost::mutex> guard(mtx_);
        balance_ -= amount;
    }
    int GetBalance() {
        boost::lock_guard<boost::mutex> guard(mtx_);
        return balance_;
    }
};

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

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

Этот подход достаточно прост в реализации и имеет привлекательную простоту. К сожалению, «простое» иногда может превратиться в «упрощенческое».

Внутренняя блокировка недостаточна для многих реальных задач синхронизации. Представьте, что вы хотите реализовать транзакцию по выводу банкоматов с классом BankAccount. Требования простые. Транзакция банкомата состоит из двух выводов - один для реальных денег и один для комиссии в размере 2 долларов США. Эти два вывода должны быть в строгой последовательности, то есть между ними не может существовать никакой другой транзакции.

Очевидная реализация является неустойчивой:

void ATMWithdrawal(BankAccount& acct, int sum) {
    acct.Withdraw(sum);
    // preemption possible
    acct.Withdraw(2);
}

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

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

void ATMWithdrawal(BankAccount& acct, int sum) {
    boost::lock_guard<boost::mutex> guard(acct.mtx_); 1
    acct.Withdraw(sum);
    acct.Withdraw(2);
}

Обратите внимание, что код выше не компилируется, поле<mtx_>является частным. У нас есть две возможности:

  • Обнародовать<mtx_>, что кажется странным
  • сделать<BankAccount>блокируемым, добавив функции блокировки / разблокировки

Мы можем добавить эти функции явно.

class BankAccount {
    boost::mutex mtx_;
    int balance_;
public:
    void Deposit(int amount) {
        boost::lock_guard<boost::mutex> guard(mtx_);
        balance_ += amount;
    }
    void Withdraw(int amount) {
        boost::lock_guard<boost::mutex> guard(mtx_);
        balance_ -= amount;
    }
    void lock() {
        mtx_.lock();
    }
    void unlock() {
        mtx_.unlock();
    }
};

или наследование от класса, который добавляет эти блокируемые функции.

<basic_lockable_adapter>класс помогает определить<BankAccount>класс как

class BankAccount
: public basic_lockable_adapter<mutex>
{
    int balance_;
public:
    void Deposit(int amount) {
        boost::lock_guard<BankAccount> guard(*this);
        balance_ += amount;
    }
    void Withdraw(int amount) {
        boost::lock_guard<BankAccount> guard(*this);
        balance_ -= amount;
    }
    int GetBalance() {
        boost::lock_guard<BankAccount> guard(*this);
        return balance_;
    }
};

Код, который не компилируется, становится

void ATMWithdrawal(BankAccount& acct, int sum) {
    boost::lock_guard<BankAccount> guard(acct);
    acct.Withdraw(sum);
    acct.Withdraw(2);
}

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

  • Ваша реализация mutex может поддерживать так называемую рекурсивную семантику mutex. Это означает, что одна и та же нить может успешно блокировать один и тот же мутекс несколько раз. В этом случае реализация работает, но имеет накладные расходы из-за ненужной блокировки. (Последовательность блокировки / разблокировки в двух вызовах отмены не требуется, но выполняется в любом случае, и это стоит времени.)
  • Ваша реализация mutex может не поддерживать рекурсивную блокировку, что означает, что, как только вы попытаетесь приобрести его во второй раз, он блокирует, поэтому функция вывода банкомата входит в ужасный тупик.

Поскольку<boost::mutex>не является рекурсивным, мы должны использовать его рекурсивную версию<boost::recursive_mutex>.

class BankAccount
: public basic_lockable_adapter<recursive_mutex>
{
    // ...
};

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

class BankAccount
    : public basic_lockable_adapter<boost:mutex> {
    int balance_;
public:
    void Deposit(int amount) {
        balance_ += amount;
    }
    void Withdraw(int amount) {
        balance_ -= amount;
    }
};

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

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

[Note]Note

Этот учебник является адаптацией статьи Андрея Александреску «Многопоточность и система типов C++» к библиотеке Boost.

Так что же делать? В идеале класс банковского счета должен делать следующее:

  • Поддерживают как модели блокировки (внутренние, так и внешние).
  • Будьте эффективными, то есть не используйте ненужную блокировку.
  • Будьте в безопасности, то есть, объектами банковского счета нельзя манипулировать без соответствующей блокировки.

Давайте сделаем полезное наблюдение: Всякий раз, когда вы блокируете банковский счет, вы делаете это с помощью объекта<lock_guard<BankAccount>>. Поворачивая это утверждение, где бы ни было<lock_guard<BankAccount>>, где-то есть также запертый<BankAccount>. Таким образом, вы можете думать об объекте<lock_guard<BankAccount>>как о разрешении.<lock_guard<BankAccount>>У вас есть право на определенные вещи.<lock_guard<BankAccount>>объект не должен быть скопирован или псевдоним (это не передаваемое разрешение).

  1. Пока разрешение еще живо, объект<BankAccount>остается заблокированным.
  2. Когда<lock_guard<BankAccount>>уничтожается,<BankAccount>мутекс освобождается.

Чистый эффект заключается в том, что в любой точке вашего кода доступ к<lock_guard<BankAccount>>объекту гарантирует, что<BankAccount>заблокирован. (Вы не знаете точно, какая<BankAccount>заблокирована, однако, вопрос, который мы рассмотрим в ближайшее время.)

Теперь давайте сделаем несколько улучшений шаблона класса<lock_guard>, определенного в Boost. Нить. Назовем улучшенную версию<strict_lock>. По сути, роль<strict_lock>заключается только в том, чтобы жить на стеке в качестве автоматической переменной.<strict_lock>должен придерживаться политики некопирования и неалия.<strict_lock>отключает копирование, делая конструктора копий и оператора присваивания частными.

template <typename Lockable>
class strict_lock  {
public:
    typedef Lockable lockable_type;
    explicit strict_lock(lockable_type& obj) : obj_(obj) {
        obj.lock(); // locks on construction
    }
    strict_lock() = delete;
    strict_lock(strict_lock const&) = delete;
    strict_lock& operator=(strict_lock const&) = delete;
    ~strict_lock() { obj_.unlock(); } //  unlocks on destruction 
    bool owns_lock(mutex_type const* l) const noexcept // strict lockers specific function 
    {
      return l == &obj_;
    }
private:
    lockable_type& obj_;
};

Молчание иногда может быть громче слов — то, что запрещено делать с<strict_lock>, так же важно, как и то, что вы можете сделать. Давайте посмотрим, что вы можете и что вы не можете сделать с<strict_lock>.

  • Вы можете создать<strict_lock<T>>только начиная с действительного Т-объекта. Заметьте, что нет другого способа создать<strict_lock<T>>.
BankAccount myAccount("John Doe", "123-45-6789");
strict_lock<BankAccount> myLock(myAccount); // ok
  • Вы не можете копировать<strict_lock>друг другу. В частности, вы не можете передать<strict_lock>s по значению функциям или вернуть их функциями:
extern strict_lock<BankAccount> Foo(); // compile-time error
extern void Bar(strict_lock<BankAccount>); // compile-time error
  • Тем не менее, вы все еще можете пройти<strict_lock>s по ссылке и из функций:
// ok, Foo returns a reference to strict_lock<BankAccount>
extern strict_lock<BankAccount>& Foo();
// ok, Bar takes a reference to strict_lock<BankAccount>
extern void Bar(strict_lock<BankAccount>&);

Все эти правила были введены в действие с одной целью - обеспечение того, что владение<strict_lock<T>>является достаточно надежной гарантией того, что

  1. Вы заперли Т-объект, и
  2. Этот объект будет разблокирован позже.

Теперь, когда у нас такой строгий<strict_lock>, как мы можем использовать его силу в определении безопасного, гибкого интерфейса для банковского счета? Идея заключается в следующем:

  • Каждая из функций интерфейса BankAccount (в нашем случае депозит и снятие средств) поставляется в двух перегруженных вариантах.
  • Одна версия сохраняет ту же подпись, что и раньше, а другая принимает дополнительный аргумент типа<strict_lock<BankAccount>>. Первая версия закрыта изнутри, вторая требует внешней блокировки. Внешняя блокировка выполняется во время компиляции, требуя код клиента для создания объекта<strict_lock<BankAccount>>.
  • BankAccount избегает вздутия кода, имея внутренние заблокированные функции, которые выполняют фактическую работу.

Небольшой код стоит 1000 слов, как говорится, так вот новый класс банковского счета:

class BankAccount
: public basic_lockable_adapter<boost::mutex>
{
    int balance_;
public:
    void Deposit(int amount, strict_lock<BankAccount>&) {
        // Externally locked
        balance_ += amount;
    }
    void Deposit(int amount) {
        strict_lock<BankAccount> guard(*this); // Internally locked
        Deposit(amount, guard);
    }
    void Withdraw(int amount, strict_lock<BankAccount>&) {
        // Externally locked
        balance_ -= amount;
    }
    void Withdraw(int amount) {
        strict_lock<BankAccount> guard(*this); // Internally locked
        Withdraw(amount, guard);
    }
};

Теперь, если вы хотите воспользоваться преимуществами внутренней блокировки, вы просто звоните<Deposit(int)>и<Withdraw(int)>. Если вы хотите использовать внешнюю блокировку, вы блокируете объект, построив<strict_lock<BankAccount>>, а затем звоните<Deposit(int, strict_lock<BankAccount>&)>и<Withdraw(int,strict_lock<BankAccount>&)>. Например, вот функция<ATMWithdrawal>, реализованная правильно:

void ATMWithdrawal(BankAccount& acct, int sum) {
    strict_lock<BankAccount> guard(acct);
    acct.Withdraw(sum, guard);
    acct.Withdraw(2, guard);
}

Эта функция имеет лучшее из обоих миров — она достаточно безопасна и эффективна одновременно.

Стоит отметить, что<strict_lock>быть шаблоном дает дополнительную безопасность по сравнению с прямым полиморфным подходом. В таком дизайне банковский счет будет происходить из интерфейса Lockable.<strict_lock>манипулирует ссылками Lockable, поэтому нет необходимости в шаблонах. Такой подход является обоснованным, однако он обеспечивает меньше гарантий времени компиляции. Наличие объекта<strict_lock>говорит только о том, что какой-то объект, полученный из Lockable, в настоящее время заблокирован. В этом случае<strict_lock<BankAccount>>будет лучше, чем<BankAccount>.

Там есть ласковое слово - я упомянул, что вывод банкоматов достаточно безопасен. На самом деле это небезопасно, потому что нет никакого принуждения, чтобы объект<strict_lock<BankAccount>>блокировал соответствующий объект банковского счета. Система типа гарантирует, что некоторый объект банковского счета заблокирован. Например, рассмотрим следующую фальшивую реализацию ATMWithdrawal:

void ATMWithdrawal(BankAccount& acct, int sum) {
    BankAccount fakeAcct("John Doe", "123-45-6789");
    strict_lock<BankAccount> guard(fakeAcct);
    acct.Withdraw(sum, guard);
    acct.Withdraw(2, guard);
}

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

Важно понимать, что может быть реализовано в системе типа C++ и что должно быть реализовано во время выполнения. Механизм, который мы установили до сих пор, гарантирует, что какой-то объект банковского счета заблокирован во время звонка<BankAccount::Withdraw(int, strict_lock<BankAccount>&)>. Мы должны обеспечить во время выполнения точно, какой объект заблокирован.

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

Во-первых, давайте уберем злобу с дороги. C — язык, требующий от программиста большого внимания и дисциплины. C++ добился определенного прогресса, спросив немного меньше из них, в то же время доверяя программисту. Эти языки не связаны со злобой (как, например, Java). В конце концов, вы можете сломать любой дизайн C/C++, просто используя оттенки «соответствующим образом» (если в этом контексте уместно соответствующее слово).

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

Использование<strict_lock>позволяет проверять время компиляции наиболее распространенного источника ошибок и время выполнения менее частой проблемы.

Давайте посмотрим, как обеспечить блокировку соответствующего объекта банковского счета. Во-первых, мы должны добавить функцию участника в шаблон класса<strict_lock>. Функция<boolstrict_lock<T>::owns_lock(Lockable*)>возвращает ссылку на заблокированный объект.

template <class Lockable> class strict_lock {
    ... as before ...
public:
    bool owns_lock(Lockable* mtx) const { return mtx==&obj_; }
};

Во-вторых, BankAccount должен использовать эту функцию сравнения заблокированного объекта с этим:

class BankAccount {
: public basic_lockable_adapter<boost::mutex>
    int balance_;
public:
    void Deposit(int amount, strict_lock<BankAccount>& guard) {
        // Externally locked
        if (!guard.owns_lock(*this))
            throw "Locking Error: Wrong Object Locked";
        balance_ += amount;
    }
// ...
};

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

Теперь предположим, что BankAccount вообще не использует собственную блокировку, а имеет только нить-нейтральную реализацию:

class BankAccount {
    int balance_;
public:
    void Deposit(int amount) {
        balance_ += amount;
    }
    void Withdraw(int amount) {
        balance_ -= amount;
    }
};

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

Скажем, у нас есть класс AccountManager, который удерживает и управляет объектом банковского счета:

class AccountManager
: public basic_lockable_adapter<boost::mutex>
{
    BankAccount checkingAcct_;
    BankAccount savingsAcct_;
    ...
};

Давайте также предположим, что, по замыслу, AccountManager должен оставаться заблокированным при доступе к своим членам. Вопрос в том, как мы можем выразить это ограничение дизайна с помощью системы типа C++? Как мы можем заявить: «У вас есть доступ к этому объекту банковского счета только после блокировки его родительского объекта управления счетом?»

Решение заключается в использовании небольшого шаблона моста<externally_locked>, который контролирует доступ к банковскому счету.

template <typename  T, typename Lockable>
class externally_locked {
    BOOST_CONCEPT_ASSERT((LockableConcept<Lockable>));
public:
    externally_locked(T& obj, Lockable& lockable)
        : obj_(obj)
        , lockable_(lockable)
    {}
    externally_locked(Lockable& lockable)
        : obj_()
        , lockable_(lockable)
    {}
    T& get(strict_lock<Lockable>& lock) {
#ifdef BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
        if (!lock.owns_lock(&lockable_)) throw lock_error(); //run time check throw if not locks the same
#endif
        return obj_;
    }
    void set(const T& obj, Lockable& lockable) {
        obj_ = obj;
        lockable_=lockable;
    }
private:
    T obj_;
    Lockable& lockable_;
};

<externally_locked>маскирует объект типа T и фактически обеспечивает полный доступ к этому объекту через функции получения и набора членов при условии, что вы передаете ссылку на<strict_lock<Owner>>объект.

Вместо того, чтобы делать<checkingAcct_>и<savingsAcct_>типа<BankAccount>,<AccountManager>держит объекты типа<externally_locked<BankAccount, AccountManager>>:

class AccountManager
    : public basic_lockable_adapter<boost::mutex>
{
public:
    typedef basic_lockable_adapter<boost::mutex> lockable_base_type;
    AccountManager()
        : checkingAcct_(*this)
        , savingsAcct_(*this)
    {}
    inline void Checking2Savings(int amount);
    inline void AMoreComplicatedChecking2Savings(int amount);
private:
    externally_locked<BankAccount, AccountManager> checkingAcct_;
    externally_locked<BankAccount, AccountManager> savingsAcct_;
};

Модель такая же, как и раньше - для доступа к объекту банковского счета, замаскированного<checkingAcct_>, нужно позвонить<get>. Чтобы позвонить<get>, вам нужно передать его<strict_lock<AccountManager>>. Единственное, о чем вы должны позаботиться, это не держать указателей или ссылок, которые вы получили, позвонив<get>. Если вы это сделаете, убедитесь, что вы не используете их после уничтожения strict_lock. То есть, если вы называете скрытые объекты, вы возвращаетесь из режима «компилятор заботится об этом» в режим «вы должны обратить внимание».

Как правило, вы используете<externally_locked>, как показано ниже. Предположим, вы хотите выполнить атомный перевод с вашего расчетного счета на ваш сберегательный счет:

void AccountManager::Checking2Savings(int amount) {
    strict_lock<AccountManager> guard(*this);
    checkingAcct_.get(guard).Withdraw(amount);
    savingsAcct_.get(guard).Deposit(amount);
}

Мы достигли двух важных целей. Во-первых, декларация<checkingAcct_>и<savingsAcct_>дает понять считывателю кода, что эта переменная защищена блокировкой на Учетном Менеджере. Во-вторых, дизайн делает невозможным манипулирование двумя счетами без фактической блокировки банковского счета.<externally_locked>Это то, что можно назвать активной документацией.

Теперь представьте, что функция AccountManager должна взять<unique_lock>, чтобы уменьшить критические области. И в какой-то момент он должен получить доступ к<checkingAcct_>. Поскольку<unique_lock>не является строгой блокировкой, следующий код не компилируется:

void AccountManager::AMoreComplicatedChecking2Savings(int amount) {
    unique_lock<AccountManager> guard(*this, defer_lock);
    if (some_condition()) {
        guard.lock();
    }
    checkingAcct_.get(guard).Withdraw(amount); // COMPILE ERROR
    savingsAcct_.get(guard).Deposit(amount);  // COMPILE ERROR
    do_something_else();
}

Нам нужен способ передачи права собственности с<unique_lock>на<strict_lock>во время работы с<savingsAcct_>, а затем восстановления права собственности на<unique_lock>.

void AccountManager::AMoreComplicatedChecking2Savings(int amount) {
    unique_lock<AccountManager> guard1(*this, defer_lock);
    if (some_condition()) {
        guard1.lock();
    }
    {
        strict_lock<AccountManager> guard(guard1);
        checkingAcct_.get(guard).Withdraw(amount);
        savingsAcct_.get(guard).Deposit(amount);
    }
    guard1.unlock();
}

Для того, чтобы сделать этот код компилируемым, мы должны хранить ссылку на блокировку или<unique_lock<Lockable>>в зависимости от конструктора. Нам также нужно сохранить, какой тип ссылки мы сохранили, и в деструкторе вызвать либо блокируемый<unlock>или восстановить право собственности.

Мне это кажется слишком сложным. Другая возможность — определить вложенный строгий класс замка. Недостатком является то, что вместо одного строгого замка у нас есть два, и нам нужно либо дублировать каждую функцию, принимая<strict_lock>, либо создавать эти шаблоны функций. Проблема с функциями шаблонов заключается в том, что мы больше не пользуемся системой типа C++. Мы должны добавить некоторую статическую метафункцию, которая проверяет, что параметр Локера является строгой блокировкой. Проблема в том, что мы не можем проверить это или можем? Метафункция<is_strict_lock>должна быть специализирована разработчиком строгой блокировки. Мы должны верить в это "сур-условие". Преимущество заключается в том, что теперь мы можем управлять более чем двумя строгими замками без изменения нашего кода. Это очень мило.

Теперь мы должны сказать, что оба класса<strict_lock>.

template <typename Locker>
struct is_strict_lock : mpl::false_ {};
template <typename Lockable>
struct is_strict_lock<strict_lock<Lockable> > : mpl::true_ {}
template <typename Locker>
struct is_strict_lock<nested_strict_lock<Locker> > : mpl::true_ {}

Позвольте мне показать, как выглядит этот класс<nested_strict_lock>и как он влияет на класс<externally_locked>и функцию<AccountManager::AMoreComplicatedFunction>.

Первый<nested_strict_lock>класс будет хранить на временной блокировке<Locker>и передавать право собственности на замок конструктору. При разрушении он восстановит собственность. Обратите внимание на использование<lock_traits>и на то, что<Locker>должен иметь ссылку на mutex, в противном случае делается исключение.

template <typename Locker >
class nested_strict_lock
    {
      BOOST_CONCEPT_ASSERT((MovableLockerConcept<Locker>));
public:
    typedef typename lockable_type<Locker>::type lockable_type;
    typedef typename syntactic_lock_traits<lockable_type>::lock_error lock_error;
    nested_strict_lock(Locker& lock)
        : lock_(lock)  // Store reference to locker
        , tmp_lock_(lock.move()) // Move ownership to temporary locker 
    {
        #ifdef BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
        if (tmp_lock_.mutex()==0) {
            lock_=tmp_lock_.move(); // Rollback for coherency purposes 
            throw lock_error();
        }
        #endif
        if (!tmp_lock_) tmp_lock_.lock(); // ensures it is locked 
    }
    ~nested_strict_lock() {
        lock_=tmp_lock_.move(); // Move ownership to nesting locker 
    }
    bool owns_lock() const { return true; }
    lockable_type* mutex() const { return tmp_lock_.mutex(); }
    bool owns_lock(lockable_type* l) const { return l==mutex(); }
private:
    Locker& lock_;
    Locker tmp_lock_;
};

Функция<externally_locked>get теперь является функцией шаблона, принимающей Locker в качестве параметров вместо<strict_lock>. Мы можем добавить тест в режиме отладки, который гарантирует, что объект заблокирован.

template <typename  T, typename Lockable>
class externally_locked {
public:
    // ...
    template <class Locker>
    T& get(Locker& lock) {
        BOOST_CONCEPT_ASSERT((StrictLockerConcept<Locker>));
        BOOST_STATIC_ASSERT((is_strict_lock<Locker>::value)); // locker is a strict locker "sur parole" 
        BOOST_STATIC_ASSERT((is_same<Lockable,
                typename lockable_type<Locker>::type>::value)); // that locks the same type 
#ifndef BOOST_THREAD_EXTERNALLY_LOCKED_DONT_CHECK_OWNERSHIP  // define BOOST_THREAD_EXTERNALLY_LOCKED_NO_CHECK_OWNERSHIP if you don't want to check locker ownership
        if (! lock ) throw lock_error(); // run time check throw if no locked 
#endif
#ifdef BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
        if (!lock.owns_lock(&lockable_)) throw lock_error();
#endif
        return obj_;
    }
};

Для этого необходимо, чтобы<AccountManager::AMoreComplicatedFunction>заменил<strict_lock>на<nested_strict_lock>.

void AccountManager::AMoreComplicatedChecking2Savings(int amount) {
    unique_lock<AccountManager> guard1(*this);
    if (some_condition()) {
        guard1.lock();
    }
    {
        nested_strict_lock<unique_lock<AccountManager> > guard(guard1);
        checkingAcct_.get(guard).Withdraw(amount);
        savingsAcct_.get(guard).Deposit(amount);
    }
    guard1.unlock();
}

В частности, библиотека предоставляет способ блокировки выполнения функции.

template <class Lockable, class Function, class... Args>
auto with_lock_guard(
    Lockable& m,
    Function&& func,
    Args&&... args
) -> decltype(func(boost::forward<Args>(args)...)) {
  boost::lock_guard<Lockable> lock(m);
  return func(boost::forward<Args>(args)...);
}

Можно использовать с регулярными функциями:

int func(int, int&);
//...
boost::mutex m;
int a;
int result = boost::with_lock_guard(m, func, 1, boost::ref(a));

Скриншоты из:bind:

int result = boost::with_lock_guard(
    m, boost::bind(func, 2, boost::ref(a))
);

или с лямбда-выражением:

int a;
int result = boost::with_lock_guard(
    m,
    [&a](int x) {
      // this scope is protected by mutex m
      a = 3;
      return x + 4;
    },
    5
);

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

Boost.Threadподдерживает четыре основных понятия для блокируемых объектов:<Lockable>,<TimedLockable>,<SharedLockable>и<UpgradeLockable>. Каждый тип mutex реализует одну или несколько из этих концепций, как и различные типы блокировок.

// #include <boost/thread/lockable_concepts.hpp> 
namespace boost
{
  template<typename L>
  class BasicLockable; // EXTENSION
}

.<BasicLockable>концептуальные модели исключительного владения. Тип<L>соответствует.<BasicLockable>требования, если следующие выражения хорошо сформированы и имеют указанную семантику<m>обозначает значение типа<L>:

Право собственности на замки, приобретенное по требованию<lock()>должен быть выпущен через призыв к<unlock()>.

Requires:

Вызывающая нить не владеет мутексом, если мутекс не рекурсивный.

Effects:

Текущий поток блокируется до тех пор, пока не будет получено право собственности на текущий поток.

Synchronization:

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

Postcondition:

Нынешняя нить принадлежит<m>.

Return type:

<void>.

Throws:

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

Error Conditions:

Operation_not_permitted: если нить не имеет привилегии выполнять операцию.

resource_deadlock_would_occur: если реализация обнаруживает, что произойдет тупик.

device_or_resource_busy: если мутекс уже заблокирован и блокировка невозможна.

Thread safety:

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

Requires:

Нынешняя нить принадлежит<m>.

Synchronization:

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

Effects:

Открывает замок на<m>текущей нитью.

Return type:

<void>.

Throws:

Ничего.

// #include <boost/thread/lockable_traits.hpp> 
namespace boost
{
  namespace sync
  {
    template<typename L>
    class is_basic_lockable;// EXTENSION
  }
}

Некоторые алгоритмы на мутексах используют эту черту через SFINAE.

Эта черта является истинной, если параметр L соответствует.<Lockable>требования.

[Warning]Warning

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

// #include <boost/thread/lockable_concepts.hpp> 
namespace boost
{
  template<typename L>
  class Lockable;
}

Тип<L>соответствует.<Lockable>если он соответствует<BasicLockable>требования и следующие выражения хорошо сформированы и имеют указанную семантику<m>обозначает значение типа<L>:

Право собственности на замки, приобретенное посредством вызова<try_lock()>, должно быть освобождено посредством вызова<unlock()>.

Requires:

Вызывающая нить не владеет мутексом, если мутекс не рекурсивный.

Effects:

Попытка получить право собственности на текущий поток без блокировки.

Synchronization:

Если<try_lock()>возвращается истинно, предыдущие<unlock()>операции на одном и том же объекте синхронизируются с этой операцией.

Note:

Поскольку<lock()>не синхронизируется с неудавшимся последующим<try_lock()>, правила видимости достаточно слабы, чтобы мало что было известно о состоянии после сбоя, даже при отсутствии ложных сбоев.

Return type:

<bool>.

Returns:

<true>в случае получения права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток владеет<m>.

Throws:

Ничего.

// #include <boost/thread/lockable_traits.hpp> 
namespace boost
{
  namespace sync
  {
    template<typename L>
    class is_lockable;// EXTENSION
  }
}

Некоторые алгоритмы на мутексах используют эту черту через SFINAE.

Эта черта является истинной, если параметр L соответствует.<Lockable>требования.

[Warning]Warning

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

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

// #include <boost/thread/lockable_traits.hpp> 
namespace boost
{
  namespace sync
  {
    template<typename L>
    class is_recursive_mutex_sur_parole: false_type; // EXTENSION
    template<>
    class is_recursive_mutex_sur_parole<recursive_mutex>: true_type; // EXTENSION
    template<>
    class is_recursive_mutex_sur_parole<timed_recursive_mutex>: true_type; // EXTENSION
  }
}

<is_recursive_mutex_sur_parole>является<false_type>по умолчанию и является специализированным для обеспечения<recursive_mutex>и<timed_recursive_mutex>.

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

// #include <boost/thread/lockable_traits.hpp> 
namespace boost
{
  namespace sync
  {
    template<typename L>
    class is_recursive_basic_lockable;// EXTENSION
  }
}

Эта черта является истинной_типом, если является_basic_lockable и является_recursive_mutex_sur_parole.

// #include <boost/thread/lockable_traits.hpp> 
namespace boost
{
  namespace sync
  {
    template<typename L>
    class is_recursive_lockable;// EXTENSION
  }
}

Эти признаки истинны_типа, если он_блокируемый и является_рекурсивным_mutex_sur_parole.

// #include <boost/thread/lockable_concepts.hpp> 
namespace boost
{
  template<typename L>
  class TimedLockable; // EXTENSION
}

Концепция<TimedLockable>уточняет концепцию<Lockable>, чтобы добавить поддержку тайм-аутов при попытке приобрести замок.

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

Переменные:

  • mобозначает величину типаL,
  • rel_timeобозначает величину инстанциацииchrono::duration, и
  • abs_timeобозначает величину инстанциацииchrono::time_point:

Выражения:

Право собственности на замки, приобретенное посредством вызова<try_lock_for>или<try_lock_until>, должно быть освобождено посредством вызова<unlock>.

Requires:

Вызывающая нить не владеет мутексом, если мутекс не рекурсивный.

Effects:

Попытка получить право собственности на текущую нить. Блоки до получения права собственности, или до достижения указанного времени. Если указанное время уже прошло, ведет себя как<try_lock()>.

Synchronization:

Если<try_lock_until()>возвращается истинно, предыдущие<unlock()>операции на одном и том же объекте синхронизируются с этой операцией.

Return type:

<bool>.

Returns:

<true>в случае получения права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток принадлежит<m>.

Throws:

Ничего.

Requires:

Вызывающая нить не владеет мутексом, если мутекс не рекурсивный.

Effects:

As-if<try_lock_until>chrono::steady_clock::теперь[][] +rel_time.

Synchronization:

Если<try_lock_for()>возвращается истинно, предыдущие<unlock()>операции на одном и том же объекте синхронизируются с этой операцией.

[Warning]Warning

Снят с 4.00. На версии 2 требовались следующие выражения, но теперь они обесценены.

Вместо этого используйте<try_lock_for>,<try_lock_until>.

Переменные:

  • rel_timeобозначает значение инстанциации неопределеннойDurationTypeарифметики, совместимой сboost::system_time, и
  • abs_timeобозначает величину инстанциацииboost::system_time:

Выражения:

Право собственности на замки, приобретенное по требованию<timed_lock()>должен быть выпущен через вызов<unlock()>.

Effects:

Попытка получить право собственности на текущую нить. Блоки до получения права собственности, или до достижения указанного времени. Если указанное время уже прошло, ведет себя как<try_lock()>.

Returns:

<true>в случае получения права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток принадлежит<m>.

Throws:

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

// #include <boost/thread/lockable_concepts.hpp> 
namespace boost
{
  template<typename L>
  class SharedLockable;  // C++14
}

Концепция<SharedLockable>является усовершенствованием концепции<TimedLockable>, которая позволяетсовместное владение, а такжеэксклюзивное владение. Это стандартная модель многократного чтения / однократной записи: в большинстве случаев один поток может иметь исключительное право собственности, и если какой-либо поток имеет исключительное право собственности, никакие другие потоки не могут иметь совместное или исключительное право собственности. Кроме того, многие потоки могут иметь совместное владение.

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

Переменные:

  • mобозначает величину типаL,
  • rel_timeобозначает величину инстанциацииchrono::duration, и
  • abs_timeобозначает величину инстанциацииchrono::time_point:

Выражения:

Право собственности на замки, приобретенное посредством вызова<lock_shared()>,<try_lock_shared()>,<try_lock_shared_for>или<try_lock_shared_until>, должно быть освобождено посредством вызова<unlock_shared()>.

Effects:

Текущий поток блокируется до тех пор, пока не будет получено совместное владение текущим потоком.

Postcondition:

Нынешняя нить имеет долевое владение<m>.

Throws:

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

Effects:

Попытка получить долевую собственность на текущий поток без блокировки.

Returns:

<true>если долевое владение было получено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток владеет<m>.

Throws:

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

Effects:

Попытка получить долевое владение для текущей нити. Блоки до тех пор, пока не будет получено долевое владение, или указанный срок истек. Если указанная продолжительность уже истекла, ведет себя как<try_lock_shared()>.

Returns:

<true>если долевое владение было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток владеет<m>.

Throws:

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

Effects:

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

Returns:

<true>если долевое владение было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток владеет<m>.

Throws:

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

Precondition:

Нынешняя нить имеет долевое владение<m>.

Effects:

Выпускает совместное владение<m>по текущему потоку.

Postcondition:

Нынешняя нить больше не имеет долевого владения<m>.

Throws:

Ничего.

[Warning]Warning

Снят с 3.00. На версии 2 требовались следующие выражения, но теперь они обесценены.

Вместо этого используйте<try_lock_shared_for>,<try_lock_shared_until>.

Переменные:

  • abs_timeобозначает величину инстанциацииboost::system_time:

Выражения:

  • m.timed_lock_shared(abs_time);

Право собственности на замки, приобретенное посредством вызова<timed_lock_shared()>, должно быть освобождено посредством вызова<unlock_shared()>.

Effects:

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

Returns:

<true>если долевое владение было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток владеет<m>.

Throws:

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

// #include <boost/thread/lockable_concepts.hpp> 
namespace boost
{
  template<typename L>
  class UpgradeLockable; // EXTENSION
}

Концепция<UpgradeLockable>является усовершенствованием концепции<SharedLockable>, которая позволяетмодернизировать собственность, а такжедолевую собственностьиэксклюзивную собственность. Это расширение модели с несколькими считывателями / одной записью, представленной концепцией<SharedLockable>: Один поток может иметьмодернизируемую собственностьв то же время, как другие имеютдолевую собственность. Нить смодернизируемой собственностьюможет в любое время попытаться модернизировать эту собственность доисключительной собственности. Если никакие другие потоки не имеют совместной собственности, обновление завершается немедленно, и поток теперь имеетэксклюзивную собственность, которая должна быть оставлена вызовом<unlock()>, как если бы он был приобретен вызовом<lock()>.

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

Право собственности также может бытьпонижено, а такжемодернизировано: эксклюзивное право собственности на реализацию концепции<UpgradeLockable>может быть понижено до модернизируемого владения или долевого владения, а модернизируемое право собственности может быть понижено до простого долевого владения.

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

Переменные:

  • mобозначает величину типаL,
  • rel_timeобозначает величину инстанциацииchrono::duration, и
  • abs_timeобозначает величину инстанциацииchrono::time_point:

Выражения:

Если 'BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERERS Определены также следующие выражения:

Право собственности на блокировку, приобретенное посредством вызова<lock_upgrade()>, должно быть освобождено посредством вызова<unlock_upgrade()>. Если тип собственности изменяется посредством вызова одной из функций<unlock_xxx_and_lock_yyy()>, право собственности должно быть освобождено посредством вызова функции разблокировки, соответствующей новому уровню владения.

Precondition:

Нить вызова не имеет права собственности на мутекс.

Effects:

Текущий поток блокирует до тех пор, пока не будет получено право собственности на обновление для текущего потока.

Postcondition:

Текущая нить имеет право собственности на обновление<m>.

Synchronization:

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

Throws:

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

Precondition:

Текущая нить имеет право собственности на обновление<m>.

Effects:

Выпускает обновление владения<m>по текущему потоку.

Postcondition:

Текущая нить больше не имеет права собственности на обновление<m>.

Synchronization:

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

Throws:

Ничего.

Precondition:

Нить вызова не имеет права собственности на мутекс.

Effects:

Попытки получить право собственности на обновление mutex для нити вызова без блокировки. Если собственность на обновление не получена, эффект отсутствует, и try_lock_upgrade() немедленно возвращается.

Returns:

<true>если право собственности на обновление было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, текущий поток имеет право собственности на обновление<m>.

Synchronization:

Если<try_lock_upgrade>()возвращает истинное, предшествующие<unlock_upgrade>()операции на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Precondition:

Нить вызова не имеет права собственности на мутекс.

Effects:

Если период клещей<rel_time>не является точно конвертируемым в период родного клеща, продолжительность округляется до ближайшего периода родного клеща. Попытки получить право собственности на блокировку для вызывающего потока в течение относительного тайм-аута, указанного<rel_time>. Если время, указанное в<rel_time>, меньше или равно<rel_time.zero()>, функция пытается получить право собственности без блокировки (как если бы позвонив<try_lock_upgrade>()). Функция возвращается в течение тайм-аута, указанного в<rel_time>, только если она получила право собственности на объект mutex.

Returns:

<true>если право собственности на обновление было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, текущий поток имеет право собственности на обновление<m>.

Synchronization:

Если<try_lock_upgrade_for>rel_timeвозвращает истинное значение, то предшествующие<unlock_upgrade>()операции на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определено на платформе Windows.

Precondition:

Нить вызова не имеет права собственности на мутекс.

Effects:

Функция пытается получить право собственности на mutex. Если<abs_time>уже прошел, функция пытается получить право собственности на обновление без блокировки (как если бы позвонив<try_lock_upgrade>()). Функция возвращается до абсолютного тайм-аута, указанного в<abs_time>, только в том случае, если она получила право собственности на объект mutex.

Returns:

<true>если право собственности на обновление было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, текущий поток имеет право собственности на обновление<m>.

Synchronization:

Если<try_lock_upgrade_until>abs_timeвозвращает истинное значение, то предыдущие<unlock_upgrade>()операции на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определено на платформе Windows.

Precondition:

Нить вызова должна удерживать общий замок на мутексе.

Effects:

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

Returns:

<true>в случае приобретения исключительного права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток имеет исключительное право собственности<m>.

Synchronization:

Если<try_unlock_shared_and_lock>()возвращает истинное значение, предыдущие<unlock>()и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

Precondition:

Призывная нить должна удерживать общий замок на мутексе.

Effects:

Если период клещей<rel_time>не является точно конвертируемым в период родного клеща, продолжительность округляется до ближайшего периода родного клеща. Функция пытается атомарно преобразовать собственность из общей в эксклюзивную для вызывающей нити в пределах относительного тайм-аута, указанного<rel_time>. Если время, указанное в<rel_time>, меньше или равно<rel_time.zero()>, функция пытается получить исключительную собственность без блокировки (как если бы позвонив<try_unlock_shared_and_lock()>). Функция возвращается в течение срока, указанного в<rel_time>, только если она приобрела исключительное право собственности на объект mutex. Чтобы это преобразование было успешным, этот поток должен быть единственным потоком, имеющим право собственности на замок в момент преобразования. Если конверсия не будет успешной, совместное владение mutex сохраняется.

Returns:

<true>в случае приобретения исключительного права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток имеет исключительное право собственности<m>.

Synchronization:

Если<try_unlock_shared_and_lock_for>rel_timeвозвращает истинное значение, предыдущие<unlock>и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

Precondition:

Призывная нить должна удерживать общий замок на мутексе.

Effects:

Функция пытается атомарно преобразовать собственность из общей в эксклюзивную для вызывающей нити в течение абсолютного тайм-аута, указанного<abs_time>. Если<abs_time>уже прошло, функция пытается получить эксклюзивное право собственности без блокировки (как будто позвонив<try_unlock_shared_and_lock()>). Функция возвращается до абсолютного тайм-аута, указанного в<abs_time>, только если она приобрела исключительное право собственности на объект mutex. Чтобы это преобразование было успешным, этот поток должен быть единственным потоком, имеющим право собственности на замок в момент преобразования. Если конверсия не будет успешной, совместное владение mutex сохраняется.

Returns:

<true>в случае приобретения исключительного права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток имеет исключительное право собственности<m>.

Synchronization:

Если<try_unlock_shared_and_lock_until>rel_timeвозвращает истинное значение, то предыдущие<unlock>и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

Precondition:

Позывная нить должна содержать эксклюзивный замок<m>.

Effects:

Атомно преобразует собственность из эксклюзивной в общую для вызывающей нити.

Postcondition:

Нынешняя нить имеет долевое владение<m>.

Synchronization:

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

Throws:

Ничего.

Precondition:

Призывная нить должна удерживать общий замок на мутексе.

Effects:

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

Returns:

<true>если право собственности на обновление было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, текущий поток имеет право собственности на обновление<m>.

Synchronization:

Если<try_unlock_shared_and_lock_upgrade>()возвращает истинное значение, предыдущие<unlock_upgrade>()и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

Precondition:

Призывная нить должна удерживать общий замок на мутексе.

Effects:

Если период<rel_time>клеща не является точно конвертируемым в период родного клеща, продолжительность округляется до ближайшего периода родного клеща. Функция пытается атомарно преобразовать собственность из общей для обновления для вызывающей нити в течение относительного тайм-аута, указанного<rel_time>. Если время, указанное в<rel_time>, меньше или равно<rel_time.zero()>, функция пытается получить право собственности на обновление без блокировки (как если бы позвонив<try_unlock_shared_and_lock_upgrade>()). Функция возвращается в течение срока, указанного в<rel_time>, только если она приобрела исключительное право собственности на объект mutex. Чтобы это преобразование было успешным, в момент преобразования не должно быть нити, удерживающей право собственности на модернизацию этого объекта. Если конверсия не увенчалась успехом, сохраняется долевое владение m.

Returns:

<true>если право собственности на обновление было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, текущий поток имеет право собственности на обновление<m>.

Synchronization:

Если<try_unlock_shared_and_lock_upgrade_for>rel_timeвозвращает истинное значение, то предыдущие<unlock_upgrade>и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

Precondition:

Призывная нить должна удерживать общий замок на мутексе.

Effects:

Функция пытается атомарно преобразовать право собственности из общего в обновление для вызывающей нити в течение абсолютного тайм-аута, указанного<abs_time>. Если<abs_time>уже прошел, функция пытается получить право собственности на обновление без блокировки (как если бы позвонив<try_unlock_shared_and_lock_upgrade>()). Функция возвращается до абсолютного тайм-аута, указанного в<abs_time>, только если она приобрела право собственности на объект mutex. Чтобы это преобразование было успешным, в момент преобразования не должно быть нити, удерживающей право собственности на модернизацию этого объекта. Если конверсия не будет успешной, совместное владение mutex сохраняется.

Returns:

<true>если право собственности на обновление было приобретено для текущей нити,<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, текущий поток имеет право собственности на обновление<m>.

Synchronization:

Если<try_unlock_shared_and_lock_upgrade_until>rel_timeвозвращает истинное значение, предыдущие<unlock_upgrade>и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

Precondition:

Текущая нить имеет исключительное право собственности<m>.

Effects:

Атомно выпускает эксклюзивное право собственности на<m>по текущему потоку и приобретает право собственности на модернизацию<m>без блокировки.

Postcondition:

Текущая нить имеет право собственности на обновление<m>.

Synchronization:

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

Throws:

Ничего.

Precondition:

Текущая нить имеет право собственности на обновление<m>.

Effects:

Атомно выпускает обновление владения<m>по текущей нити и приобретает исключительное право собственности<m>. Если какие-либо другие потоки имеют общую собственность, блоки до тех пор, пока не будет приобретена исключительная собственность.

Postcondition:

Текущая нить имеет исключительное право собственности<m>.

Synchronization:

Эта операция синхронизируется с предыдущими<unlock_shared()>()и последующими операциями блокировки, которые получают право собственности на тот же объект.

Throws:

Ничего.

Precondition:

Вызывающая нить должна удерживать блокировку обновления на мутексе.

Effects:

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

Returns:

<true>в случае приобретения исключительного права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток имеет исключительное право собственности<m>.

Synchronization:

Если<try_unlock_upgrade_and_lock>()возвращает истинное значение, предыдущие<unlock>()и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определено на платформе Windows.

Precondition:

Вызывающая нить должна удерживать блокировку обновления на мутексе.

Effects:

Если период клещей<rel_time>не является точно конвертируемым в период родного клеща, продолжительность округляется до ближайшего периода родного клеща. Функция пытается атомарно преобразовать собственность из модернизированной в эксклюзивную для вызывающей нити в течение относительного тайм-аута, указанного в<rel_time>. Если время, указанное в<rel_time>, меньше или равно<rel_time.zero()>, функция пытается получить исключительную собственность без блокировки (как если бы позвонив<try_unlock_upgrade_and_lock>()). Функция возвращается в течение срока, указанного в<rel_time>, только если она приобрела исключительное право собственности на объект mutex. Для того, чтобы это преобразование было успешным, эта нить должна быть единственной нитью, имеющей право собственности на замок в момент преобразования. Если конверсия не увенчалась успехом, сохраняется право собственности на обновление m.

Returns:

<true>в случае приобретения исключительного права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток имеет исключительное право собственности<m>.

Synchronization:

Если<try_unlock_upgrade_and_lock_for>rel_time)возвращает истинное значение, то предыдущие<unlock>()и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определено на платформе Windows.

Precondition:

Вызывающая нить должна удерживать блокировку обновления на мутексе.

Effects:

Функция пытается атомарно преобразовать собственность из модернизированной в эксклюзивную для вызывающей нити в течение абсолютного тайм-аута, указанного<abs_time>. Если<abs_time>уже прошло, функция пытается получить исключительную собственность без блокировки (как если бы позвонив<try_unlock_upgrade_and_lock>()). Функция возвращается до абсолютного тайм-аута, указанного в<abs_time>, только если она получила исключительное право собственности на объект mutex. Для того, чтобы это преобразование было успешным, эта нить должна быть единственной нитью, имеющей право собственности на замок в момент преобразования. Если конверсия не увенчалась успехом, сохраняется право собственности на обновление m.

Returns:

<true>в случае приобретения исключительного права собственности на текущую нить<false>в противном случае.

Postcondition:

Если вызов возвращается<true>, то текущий поток имеет исключительное право собственности<m>.

Synchronization:

Если<try_unlock_upgrade_and_lock_for>rel_time)возвращает истинное значение, то предыдущие<unlock>()и последующие операции блокировки на одном и том же объекте синхронизируются с этой операцией.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определено на платформе Windows.

Precondition:

Текущая нить имеет право собственности на обновление<m>.

Effects:

Атомно выпускает обновление владения<m>по текущему потоку и приобретает долевое владение<m>без блокировки.

Postcondition:

Нынешняя нить имеет долевое владение<m>.

Synchronization:

Эта операция синхронизируется с предыдущими<unlock_shared()>и последующими операциями блокировки, которые получают право собственности на тот же объект.

Throws:

Ничего.

// #include <boost/thread/locks.hpp> 
// #include <boost/thread/locks_options.hpp> 
namespace boost
{
  struct defer_lock_t {};
  struct try_to_lock_t {};
  struct adopt_lock_t {};
  constexpr defer_lock_t defer_lock;
  constexpr try_to_lock_t try_to_lock;
  constexpr adopt_lock_t adopt_lock;
#include <boost/thread/locks.hpp>
#include <boost/thread/locks_options.hpp>
struct defer_lock_t {};
struct try_to_lock_t {};
struct adopt_lock_t {};
const defer_lock_t defer_lock;
const try_to_lock_t try_to_lock;
const adopt_lock_t adopt_lock;

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

  • defer_lock_t: используется для построения прицельного замка без его блокировки.
  • try_to_lock_t: используется для построения замка с прицелом, пытающегося его заблокировать.
  • adopt_lock_t: используется для построения замка с прицелом, не блокируя его, но принимая право собственности.
// #include <boost/thread/locks.hpp> 
// #include <boost/thread/lock_guard.hpp> 
namespace boost
{
  template<typename Lockable>
  class lock_guard
#if ! defined BOOST_THREAD_NO_MAKE_LOCK_GUARD
  template <typename Lockable>
  lock_guard<Lockable> make_lock_guard(Lockable& mtx); // EXTENSION
  template <typename Lockable>
  lock_guard<Lockable> make_lock_guard(Lockable& mtx, adopt_lock_t); // EXTENSION
#endif
}
// #include <boost/thread/locks.hpp>
// #include <boost/thread/lock_guard.hpp> 
template<typename Lockable>
class lock_guard
{
public:
    explicit lock_guard(Lockable& m_);
    lock_guard(Lockable& m_,boost::adopt_lock_t);
    ~lock_guard();
};

<boost::lock_guard>очень прост: на строительстве он приобретает право собственности на реализацию<Lockable>концепции, поставляемой в качестве параметра конструктора. При разрушении собственность освобождается. Это обеспечивает простую блокировку в стиле RAII.<Lockable>объект, облегчающий блокировку и разблокировку. Кроме того,<lock_guard(Lockable& m,boost::adopt_lock_t)>конструктордопускает.<boost::lock_guard>возражают против владения замком, уже удерживаемым текущей нитью.

Effects:

Сохраняется ссылка на<m>. Призывает<m.lock()>.

Throws:

Всякое исключение бросается призывом к<m.lock()>.

Precondition:

Нынешняя нить имеет замок<m>, эквивалентный тому, который получен вызовом<m.lock()>.

Effects:

Содержит ссылку на<m>. Приобретает право собственности на состояние замка<m>.

Throws:

Ничего.

Effects:

<m.unlock()>на<Lockable>объект передан конструктору.

Throws:

Ничего.

template <typename Lockable>
lock_guard<Lockable> make_lock_guard(Lockable& m); // EXTENSION

Returns:

a lock_guard как инициализированный<{m}>.

Throws:

Всякое исключение бросается призывом к<m.lock()>.

template <typename Lockable>
lock_guard<Lockable> make_lock_guard(Lockable& m, adopt_lock_t); // EXTENSION

Returns:

a lock_guard как инициализированный<{m,adopt_lock}>.

Throws:

Всякое исключение бросается призывом к<m.lock()>.

// #include <boost/thread/with_lock_guard.hpp>
namespace boost
{
  template <class Lockable, class Function, class... Args>
  auto with_lock_guard(Lockable& m, Function&& func, Args&&... args) -> decltype(func(boost::forward<Args>(args)...));
}
template <class Lockable, class Function, class... Args>
auto with_lock_guard(
    Lockable& m,
    Function&& func,
    Args&&... args
) -> decltype(func(boost::forward<Args>(args)...));

Precondition:

m must be in unlocked state

Effects:

call func in scope locked by m

Returns:

Результат звонка<func(args...)>

Throws:

Any exception thrown by the call to m.lock and func(args...)

Postcondition:

m is in unlocked state

Limitations:

Without c++11 variadic templates support number of arguments is limited to 4

Без ссылок на rvalue поддержка метода вызова класса с<boost::bind>должна быть постоянной.

Для правильной работы с лямбда-макро<BOOST_RESULT_OF_USE_DECLTYPE>может потребоваться определение

// #include <boost/thread/lock_concepts.hpp> 
namespace boost
{
  template<typename Lock>
  class StrictLock;
}

StrictLock - это замок, который гарантирует, что связанный с ним mutex заблокирован в течение срока службы замка.

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

  • L::mutex_type
  • is_strict_lock<L>
  • cl.owns_lock(m);

и BasicLockable

где

  • clобозначает значение типаLconst&,
  • mобозначает значение типаL::mutex_typeconst*,

Тип L::mutex_type обозначает mutex, который запирается этим замком.

Как семантический "гарантирует, что связанный мутекс запирается в течение срока службы замка. "не может быть описана синтаксическими требованиями а<is_strict_lock_sur_parole>черта должна быть специализирована пользователем, определяющим блокировку таким образом, чтобы верно было следующее утверждение:

is_strict_lock_sur_parole<L>::value == true

Return Type:

bool

Returns:

Whether the strict lock is locking the mutex m

Throws:

Ничего.

Следующие классы являются моделями<StrictLock>:

  • strict_lock: обеспечивается строительством;
  • вложенный_strict_lock: "sur parole", поскольку пользователь может использовать adopt_lock_t при перегрузке конструктора уникальных_lock без блокировки mutex,
  • boost::lock_guard: «sur parole», поскольку пользователь может использовать перегрузку конструктора adopt_lock_t без блокировки mutex.
// #include <boost/thread/locks.hpp> 
// #include <boost/thread/lock_types.hpp> 
namespace boost
{
  template<typename Lockable>
  class unique_lock;
  template<typename Mutex>
  void swap(unique_lock <Mutex>& lhs, unique_lock <Mutex>& rhs);
  template<typename Lockable>
  class shared_lock; // C++14
  template<typename Mutex>
  void swap(shared_lock<Mutex>& lhs,shared_lock<Mutex>& rhs); // C++14
  template<typename Lockable>
  class upgrade_lock; // EXTENSION
  template<typename Mutex>
  void swap(upgrade_lock <Mutex>& lhs, upgrade_lock <Mutex>& rhs); // EXTENSION
  template <class Mutex>
  class upgrade_to_unique_lock; // EXTENSION
}
// #include <boost/thread/locks.hpp>
// #include <boost/thread/lock_types.hpp> 
template<typename Lockable>
class unique_lock
{
public:
    typedef Lockable mutex_type;
    unique_lock() noexcept;
    explicit unique_lock(Lockable& m_);
    unique_lock(Lockable& m_,adopt_lock_t);
    unique_lock(Lockable& m_,defer_lock_t) noexcept;
    unique_lock(Lockable& m_,try_to_lock_t);
#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION
    unique_lock(shared_lock<mutex_type>&& sl, try_to_lock_t); // C++14 
    template <class Clock, class Duration>
    unique_lock(shared_lock<mutex_type>&& sl,
                const chrono::time_point<Clock, Duration>& abs_time); // C++14
    template <class Rep, class Period>
    unique_lock(shared_lock<mutex_type>&& sl,
                const chrono::duration<Rep, Period>& rel_time); // C++14
#endif
    template <class Clock, class Duration>
    unique_lock(Mutex& mtx, const chrono::time_point<Clock, Duration>& t);
    template <class Rep, class Period>
    unique_lock(Mutex& mtx, const chrono::duration<Rep, Period>& d);
    ~unique_lock();
    unique_lock(unique_lock const&) = delete;
    unique_lock& operator=(unique_lock const&) = delete;
    unique_lock(unique_lock<Lockable>&& other) noexcept;
    explicit unique_lock(upgrade_lock<Lockable>&& other) noexcept; // EXTENSION
    unique_lock& operator=(unique_lock<Lockable>&& other) noexcept;
    void swap(unique_lock& other) noexcept;
    Lockable* release() noexcept;
    void lock();
    bool try_lock();
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();
    explicit operator bool() const noexcept;
    bool owns_lock() const noexcept;
    mutex_type* mutex() const noexcept;
#if defined BOOST_THREAD_USE_DATE_TIME || defined BOOST_THREAD_DONT_USE_CHRONO
    unique_lock(Lockable& m_,system_time const& target_time);
    template<typename TimeDuration>
    bool timed_lock(TimeDuration const& relative_time);
    bool timed_lock(::boost::system_time const& absolute_time);
#endif
};

<boost::unique_lock>является более сложным, чем<boost::lock_guard>: он не только предусматривает блокировку в стиле RAII, он также позволяет откладывать приобретение замка до тех пор, пока функция<lock()>члена не будет названа явно, или пытается приобрести замок неблокирующим способом, или с тайм-аутом. Следовательно,<unlock()>вызывается в деструктор только в том случае, если объект блокировки запер<Lockable>объект, или иным образом принят замок на<Lockable>объект.

Специализации<boost::unique_lock>модели<TimedLockable>концепции, если поставляемый<Lockable>тип сам модели<TimedLockable>концепции (например,<boost::unique_lock<boost::timed_mutex>>), или<Lockable>концепции, если поставляемый<Lockable>тип сам модели<Lockable>концепции (например,<boost::unique_lock<boost::mutex>>), или<BasicLockable>концепции, если поставляемый<Lockable>тип сам модели<BasicLockable>концепции.

Пример<boost::unique_lock>, как говорят,владеетсостоянием замка<Lockable><m>, если<mutex()>возвращает указатель<m<owns_lock()>возвращает<true>. Если объект, которыйвладеет, разрушается состояние замка<Lockable>, то деструктор вызовет<mutex()->unlock()>.

Функции<boost::unique_lock>не являются безвредными. В частности,<boost::unique_lock>предназначен для моделирования владения объектом<Lockable>конкретной нитью, и функции члена, которые освобождают право собственности на состояние замка (включая деструктор), должны быть вызваны той же нитью, которая приобрела право собственности на состояние замка.

Effects:

Создает объект блокировки без связанного мутекса.

Postcondition:

<owns_lock()>возвращается<false>.<mutex()>возвращается<NULL>.

Throws:

Ничего.

Effects:

Сохраняется ссылка на<m>. Призывает<m.lock()>.

Postcondition:

<owns_lock()>возвращается<true><mutex()>возвращается<&m>.

Throws:

Всякое исключение бросается призывом к<m.lock()>.

Precondition:

Текущая нить имеет эксклюзивный замок<m>.

Effects:

Содержит ссылку на<m>. Приобретает право собственности на состояние замка<m>.

Postcondition:

<owns_lock()>возвращается<true><mutex()>возвращается<&m>.

Throws:

Ничего.

Effects:

Содержит ссылку на<m>.

Postcondition:

<owns_lock()>возвращается<false>.<mutex()>возвращается<&m>.

Throws:

Ничего.

Effects:

Содержит ссылку на<m>.<m.try_lock()>и принимает на себя право собственности на состояние блокировки, если вызов возвращается<true>.

Postcondition:

<mutex()>возвращается<&m>. Если призыв к<try_lock()>вернулся<true>, то<owns_lock()>возвращается<true>, в противном случае<owns_lock()>возвращается<false>.

Throws:

Ничего.

Requires:

Поставляемый<Mutex>тип должен реализовывать<try_unlock_shared_and_lock>().

Effects:

Построен объект типа<boost::unique_lock>. Пусть<pm>будет указателем на мутекс и<owns>состояние собственности. Инициирует<pm>с nullptr и<owns>с ложным. Если<sl.owns_lock()>[]возвращает<false>, то<pm>устанавливает обратное значение<sl.release()>.<sl.owns_lock()>[]возвращает<true>, и в этом случае, если<sl.mutex()->try_unlock_shared_and_lock()>возвращает<true>, устанавливает<pm>на величину, возвращаемую<sl.release()>, и устанавливает<owns>на<true>.

Note:

Если<sl.owns_lock()>возвращается<true>и<sl.mutex()->try_unlock_shared_and_lock()>возвращается<false>,<sl>не изменяется.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

template <class Clock, class Duration>
unique_lock(shared_lock<mutex_type>&& sl,
            const chrono::time_point<Clock, Duration>& abs_time);

Requires:

Поставляемый<Mutex>тип должен осуществлять<try_unlock_shared_and_lock_until>abs_time.

Effects:

Конструирует объект типа<boost::unique_lock>, инициализируя<pm>с<nullptr>и<owns>с<false>. Если<sl. owns_lock()>[]возвращает<false>, то<pm>устанавливает значение возврата<sl.release()>. Другой<sl.owns_lock()>[]возвращает<true>, и в этом случае, если<sl.mutex()->try_unlock_shared_and_lock_until>abs_timeвозвращает<true>, устанавливает<pm>на величину, возвращаемую<sl.release()>и устанавливает<owns>на<true>.

Note:

Если<sl.owns_lock()>возвращается<true>и<sl.mutex()-> try_unlock_shared_and_lock_until>abs_timeвозвращается<false>,<sl>не изменяется.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

template <class Rep, class Period>
unique_lock(shared_lock<mutex_type>&& sl,
            const chrono::duration<Rep, Period>& rel_time)

Requires:

Поставляемый<Mutex>тип должен осуществлять<try_unlock_shared_and_lock_for>rel_time.

Effects:

Построение объекта типа<boost::unique_lock>, инициализация<pm>с<nullptr>и<owns>с<false>. Если<sl. owns_lock()>()возвращает<false>, то<pm>устанавливает обратное значение<sl.release()>. Еще<sl.owns_lock()>возвращает<true>, и в этом случае, если<sl.mutex()->try_unlock_shared_and_lock_for>rel_timeвозвращает<true>, устанавливает<pm>на величину, возвращаемую<sl.release()>, и устанавливает<owns>на<true>.

Note:

Если<sl.owns_lock()>возвращается<true>и<sl.mutex()-> try_unlock_shared_and_lock_for>rel_timeвозвращается<false>,<sl>не изменяется.

Postcondition:

.

Throws:

Ничего.

Notes:

Доступно только в том случае, если<BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION>и<BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN>определены на платформе Windows.

Effects:

Сохраняется ссылка на<m>. Призывает<m.timed_lock(abs_time)>и принимает на себя право собственности на состояние блокировки, если вызов возвращается<true>.

Postcondition:

<mutex()>возвращается<&m>. Если звонок в<timed_lock()>вернулся<true>, то<owns_lock()>возвращается<true>, в противном случае<owns_lock()>возвращается<false>.

Throws:

Любые исключения, вызванные призывом<m.timed_lock(abs_time)>.

Effects:

Сохраняет ссылку на<m>. Призывает<m.try_lock_until>abs_timeи принимает на себя право собственности на состояние блокировки, если вызов возвращается<true>.

Postcondition:

<mutex()>возвращается<&m>. Если звонок в<try_lock_until>вернулся<true>, то<owns_lock()>возвращается<true>, в противном случае<owns_lock()>возвращается<false>.

Throws:

Любые исключения, вызванные призывом<m.try_lock_until>abs_time.

Effects:

Сохраняет ссылку на<m>.Призывает<m.try_lock_for>rel_timeи принимает на себя право собственности на состояние блокировки, если звонок возвращается<true>.

Postcondition:

<mutex()>возвращается<&m>. Если призыв к<try_lock_for>вернулся<true>, то<owns_lock()>возвращается<true>, в противном случае<owns_lock()>возвращается<false>.

Throws:

Любые исключения, вызванные призывом<m.try_lock_for>rel_time.

Effects:

Призывает<mutex()><->><unlock()>, если<owns_lock()>возвращается<true>.

Throws:

Ничего.

Returns:

<true>, если<*this>владеет замком на<Lockable>объекте, связанном с<*this>.

Throws:

Ничего.

Returns:

Указатель на<Lockable>объект, связанный с<*this>, или<NULL>, если такого объекта нет.

Throws:

Ничего.

Returns:

<owns_lock()>().

Throws:

Ничего.

Effects:

Связь между<*this<Lockable>объектом удаляется, не влияя на состояние блокировки<Lockable>объекта. Если<owns_lock()>вернули бы<true>, то ответственность за обеспечение правильной разблокировки<Lockable>лежит на телефонном коде.

Returns:

Указатель на<Lockable>объект, связанный с<*this>в точке вызова, или<NULL>, если такого объекта нет.

Throws:

Ничего.

Postcondition:

<*this>больше не связан с каким-либо<Lockable>объектом.<mutex()>возвращает<NULL<owns_lock()>возвращает<false>.

// #include <boost/thread/locks.hpp>
// #include <boost/thread/lock_types.hpp> 
template<typename Lockable>
class shared_lock
{
public:
    typedef Lockable mutex_type;
    // Shared locking
    shared_lock();
    explicit shared_lock(Lockable& m_);
    shared_lock(Lockable& m_,adopt_lock_t);
    shared_lock(Lockable& m_,defer_lock_t);
    shared_lock(Lockable& m_,try_to_lock_t);
    template <class Clock, class Duration>
    shared_lock(Mutex& mtx, const chrono::time_point<Clock, Duration>& t);
    template <class Rep, class Period>
    shared_lock(Mutex& mtx, const chrono::duration<Rep, Period>& d);
    ~shared_lock();
    shared_lock(shared_lock const&) = delete;
    shared_lock& operator=(shared_lock const&) = delete;
    shared_lock(shared_lock<Lockable> && other);
    shared_lock& operator=(shared_lock<Lockable> && other);
    void lock();
    bool try_lock();
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();
    // Conversion from upgrade locking
    explicit shared_lock(upgrade_lock<Lockable> && other); // EXTENSION
    // Conversion from exclusive locking
    explicit shared_lock(unique_lock<Lockable> && other);
    // Setters
    void swap(shared_lock& other);
    mutex_type* release() noexcept;
    // Getters
    explicit operator bool() const;
    bool owns_lock() const;
    mutex_type mutex() const;
#if defined BOOST_THREAD_USE_DATE_TIME || defined BOOST_THREAD_DONT_USE_CHRONO
    shared_lock(Lockable& m_,system_time const& target_time);
    bool timed_lock(boost::system_time const& target_time);
#endif
};

Как и<boost::unique_lock>,<boost::shared_lock>модели концепта<Lockable>, но вместо приобретения уникального права собственности на поставленный<Lockable>объект, блокировка экземпляра<boost::shared_lock>приобретает долевое владение.

Как и<boost::unique_lock>, он не только предусматривает блокировку в стиле RAII, но также позволяет откладывать приобретение замка до тех пор, пока функция<lock()>члена не будет названа явно, или пытается приобрести замок неблокирующим способом, или с тайм-аутом. Следовательно,<unlock()>вызывается в деструктор только в том случае, если блокирующий объект запер<Lockable>объект или иным образом принял блокировку на<Lockable>объект.

Пример<boost::shared_lock>, как говорят,владеетсостоянием замка<Lockable><m>, если<mutex()>возвращает указатель<m<owns_lock()>возвращает<true>. Если объект, которыйвладеет, блокирует состояние<Lockable>объекта, то деструктор вызовет<mutex()->unlock_shared()>.

Функции<boost::shared_lock>не являются безвредными. В частности,<boost::shared_lock>предназначен для моделирования совместного владения объектом<Lockable>конкретной нитью, и функции члена, которые освобождают право собственности на состояние замка (включая деструктор), должны быть вызваны той же нитью, которая приобрела право собственности на состояние замка.

Effects:

Создает объект блокировки без связанного мутекса.

Postcondition:

<owns_lock()>возвращается<false>.<mutex()>возвращается<NULL>.

Throws:

Ничего.

Effects:

Сохраняется ссылка на<m>. Призывает<m.lock_shared()>.

Postcondition:

<owns_lock()>возвращается<true>.<mutex()>возвращается<&m>.

Throws:

Любое исключение, вызванное призывом<m.lock_shared()>.

Precondition:

Текущая нить имеет эксклюзивный замок<m>.

Effects:

Содержит ссылку на<m>. Приобретает право собственности на состояние замка<m>.

Postcondition:

<owns_lock()>возвращается<true>.<mutex()>возвращается<&m>.

Throws:

Ничего.

Effects:

Содержит ссылку на<m>.

Postcondition:

<owns_lock()>возвращается<false>.<mutex()>возвращается<&m>.

Throws:

Ничего.

Effects:

Сохраняется ссылка на<m>. Призывает<m.try_lock_shared()>и принимает на себя право собственности на состояние блокировки, если вызов возвращается<true>.

Postcondition:

<mutex()>возвращается<&m>. Если призыв к<try_lock_shared()>вернулся<true>, то<owns_lock()>возвращается<true>, в противном случае<owns_lock()>возвращается<false>.

Throws:

Ничего.

Effects:

Сохраняется ссылка на<m>. Призывает<m.timed_lock(abs_time)>и принимает на себя право собственности на состояние блокировки, если вызов возвращается<true>.

Postcondition:

<mutex()>возвращается<&m>. Если призыв к<timed_lock_shared()>вернулся<true>, то<owns_lock()>возвращается<true>, в противном случае<owns_lock()>возвращается<false>.

Throws:

Любые исключения, вызванные призывом<m.timed_lock(abs_time)>.

Effects:

Призывает<mutex()><->><unlock_shared()>, если<owns_lock()>возвращается<true>.

Throws:

Ничего.

Returns:

<true>, если<*this>владеет замком на<Lockable>объекте, связанном с<*this>.

Throws:

Ничего.

Returns:

Указатель на<Lockable>объект, связанный с<*this>, или<NULL>, если такого объекта нет.

Throws:

Ничего.

Returns:

<owns_lock()>.

Throws:

Ничего.

Effects:

Связь между<*this<Lockable>объектом удаляется, не влияя на состояние блокировки<Lockable>объекта. Если<owns_lock()>вернули бы<true>, то ответственность за обеспечение правильной разблокировки<Lockable>лежит на коде вызова.

Returns:

Указатель на<Lockable>объект, связанный с<*this>в точке вызова, или<NULL>, если такого объекта нет.

Throws:

Ничего.

Postcondition:

<*this>больше не связан с каким-либо<Lockable>объектом.<mutex()>возвращает<NULL<owns_lock()>возвращает<false>.

// #include <boost/thread/locks.hpp>
// #include <boost/thread/lock_types.hpp> 
template<typename Lockable>
class upgrade_lock
{
public:
    typedef Lockable mutex_type;
    // Upgrade locking
    upgrade_lock();
    explicit upgrade_lock(mutex_type& m_);
    upgrade_lock(mutex_type& m, defer_lock_t) noexcept;
    upgrade_lock(mutex_type& m, try_to_lock_t);
    upgrade_lock(mutex_type& m, adopt_lock_t);
    template <class Clock, class Duration>
    upgrade_lock(mutex_type& m,
                 const chrono::time_point<Clock, Duration>& abs_time);
    template <class Rep, class Period>
    upgrade_lock(mutex_type& m,
                 const chrono::duration<Rep, Period>& rel_time);
    ~upgrade_lock();
    upgrade_lock(const upgrade_lock& other) = delete;
    upgrade_lock& operator=(const upgrade_lock<Lockable> & other) = delete;
    upgrade_lock(upgrade_lock<Lockable> && other);
    upgrade_lock& operator=(upgrade_lock<Lockable> && other);
    void lock();
    bool try_lock();
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();
#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION
   // Conversion from shared locking
    upgrade_lock(shared_lock<mutex_type>&& sl, try_to_lock_t);
    template <class Clock, class Duration>
    upgrade_lock(shared_lock<mutex_type>&& sl,
                   const chrono::time_point<Clock, Duration>& abs_time);
    template <class Rep, class Period>
    upgrade_lock(shared_lock<mutex_type>&& sl,
                   const chrono::duration<Rep, Period>& rel_time);
#endif
    // Conversion from exclusive locking
    explicit upgrade_lock(unique_lock<Lockable> && other);
    // Setters
    void swap(upgrade_lock& other);
    mutex_type* release() noexcept;
    // Getters
    explicit operator bool() const;
    bool owns_lock() const;
    mutex_type mutex() const;
};

Подобно<boost::unique_lock>,<boost::upgrade_lock>модели<Lockable>концепт, но вместо приобретения уникального права собственности на поставленный<Lockable>объект, блокировка экземпляра<boost::upgrade_lock>приобретает право собственности на модернизацию.

Как и<boost::unique_lock>, он не только предусматривает блокировку в стиле RAII, но также позволяет откладывать приобретение замка до тех пор, пока функция<lock()>члена не будет названа явно, или пытается приобрести замок неблокирующим способом, или с тайм-аутом. Следовательно,<unlock()>вызывается в деструктор только в том случае, если блокирующий объект запер<Lockable>объект или иным образом принял блокировку на<Lockable>объект.

Пример<boost::upgrade_lock>, как говорят,владеетсостоянием замка<Lockable><m>, если<mutex()>возвращает указатель<m<owns_lock()>возвращает<true>. Если объект, которому принадлежит, будет уничтожен, то деструктор вызовет<mutex()->unlock_upgrade()>.

Функции члена<boost::upgrade_lock>не являются безвредными. В частности,<boost::upgrade_lock>предназначен для моделирования права собственности на обновление объекта<UpgradeLockable>посредством конкретной нити, и функции-члены, которые освобождают право собственности на состояние замка (включая деструктор), должны быть вызваны той же нитью, которая приобрела право собственности на состояние замка.

// #include <boost/thread/locks.hpp>
// #include <boost/thread/lock_types.hpp> 
template <class Lockable>
class upgrade_to_unique_lock
{
public:
    typedef Lockable mutex_type;
    explicit upgrade_to_unique_lock(upgrade_lock<Lockable>& m_);
    ~upgrade_to_unique_lock();
    upgrade_to_unique_lock(upgrade_to_unique_lock const& other) = delete;
    upgrade_to_unique_lock& operator=(upgrade_to_unique_lock<Lockable> const& other) = delete;
    upgrade_to_unique_lock(upgrade_to_unique_lock<Lockable> && other);
    upgrade_to_unique_lock& operator=(upgrade_to_unique_lock<Lockable> && other);
    void swap(upgrade_to_unique_lock& other);
    explicit operator bool() const;
    bool owns_lock() const;
    mutex_type* mutex() const;
};

<boost::upgrade_to_unique_lock>допускает временную модернизацию<boost::upgrade_lock>до исключительного владения. При строительстве со ссылкой на экземпляр<boost::upgrade_lock>, если этот экземпляр имеет право собственности на некоторые объекты<Lockable>, это право собственности модернизируется до исключительного права собственности. Когда<boost::upgrade_to_unique_lock>экземпляр уничтожен, право собственности на<Lockable>понижено доправо собственности на модернизацию.

class MutexType::scoped_try_lock
{
private:
    MutexType::scoped_try_lock(MutexType::scoped_try_lock<MutexType>& other);
    MutexType::scoped_try_lock& operator=(MutexType::scoped_try_lock<MutexType>& other);
public:
    MutexType::scoped_try_lock();
    explicit MutexType::scoped_try_lock(MutexType& m);
    MutexType::scoped_try_lock(MutexType& m_,adopt_lock_t);
    MutexType::scoped_try_lock(MutexType& m_,defer_lock_t);
    MutexType::scoped_try_lock(MutexType& m_,try_to_lock_t);
    MutexType::scoped_try_lock(MutexType::scoped_try_lock<MutexType>&& other);
    MutexType::scoped_try_lock& operator=(MutexType::scoped_try_lock<MutexType>&& other);
    void swap(MutexType::scoped_try_lock&& other);
    void lock();
    bool try_lock();
    void unlock();
    MutexType* mutex() const;
    MutexType* release();
    explicit operator bool() const;
    bool owns_lock() const;
};

Типовое обозначение<scoped_try_lock>для каждого отдельного<MutexType>является типизированным для класса с предшествующим определением. Семантика каждого конструктора и функции-члена идентична семантике<boost::unique_lock<MutexType>>для того же<MutexType>, за исключением того, что конструктор, который берет одну ссылку на мутекс, будет называть<m.try_lock()>, а не<m.lock()>.

// #include <boost/thread/locks.hpp> 
// #include <boost/thread/strict_lock.hpp> 
namespace boost
{
  template<typename Lockable>
  class strict_lock;
  template <typename Lock>
  class nested_strict_lock;
  template <typename Lockable>
  struct is_strict_lock_sur_parole<strict_lock<Lockable> >;
  template <typename Lock>
  struct is_strict_lock_sur_parole<nested_strict_lock<Lock> >;
#if ! defined BOOST_THREAD_NO_MAKE_STRICT_LOCK
  template <typename Lockable>
  strict_lock<Lockable> make_strict_lock(Lockable& mtx);
#endif
#if ! defined BOOST_THREAD_NO_MAKE_NESTED_STRICT_LOCK
  template <typename Lock>
  nested_strict_lock<Lock> make_nested_strict_lock(Lock& lk);
#endif
}
// #include <boost/thread/locks.hpp>
// #include <boost/thread/strict_lock.hpp> 
template<typename BasicLockable>
class strict_lock
{
public:
    typedef BasicLockable mutex_type;
    strict_lock(strict_lock const& m_) = delete;
    strict_lock& operator=(strict_lock const& m_) = delete;
    explicit strict_lock(mutex_type& m_);
    ~strict_lock();
    bool owns_lock(mutex_type const* l) const noexcept;
};

<strict_lock>является образцом<StrictLock>.

<strict_lock>является простейшим<StrictLock>: на строительстве он приобретает право собственности на реализацию концепции<BasicLockable>, поставляемой в качестве параметра конструктора. При разрушении собственность освобождается. Это обеспечивает простую блокировку объекта<BasicLockable>в стиле RAII для облегчения блокировки и разблокировки.

See also boost::lock_guard

Effects:

Сохраняется ссылка на<m>. Призывает<m.lock()>.

Throws:

Всякое исключение бросается призывом к<m.lock()>.

Effects:

<m.unlock()>на<Lockable>объект передан конструктору.

Throws:

Ничего.

// #include <boost/thread/locks.hpp>
// #include <boost/thread/strict_lock.hpp> 
template<typename Lock>
class nested_strict_lock
{
public:
    typedef BasicLockable mutex_type;
    nested_strict_lock(nested_strict_lock const& m_) = delete;
    nested_strict_lock& operator=(nested_strict_lock const& m_) = delete;
    explicit nested_strict_lock(Lock& lk),
    ~nested_strict_lock() noexcept;
    bool owns_lock(mutex_type const* l) const noexcept;
};

<nested_strict_lock>является образцом<StrictLock>.

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

See also strict_lock, boost::unique_lock

Requires:

<lk.mutex() !=null_ptr>.

Effects:

Сохраняет ссылку на параметр блокировки<lk>и принимает на себя право собственности на него. Если замок не принадлежит Мутексу, заприте его.

Postcondition:

<owns_lock(lk.mutex())>.

Throws:

- lock_error when BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED lk.mutex() == null_ptr

Любое исключение, которое @c lk.lock() может бросить.

Effects:

Восстанавливает владение гнездовым замком.

Return:

Если этот замок запирает этот мутекс.

template <typename Lockable>
strict_lock<Lockable> make_strict_lock(Lockable& m); // EXTENSION

Returns:

Строгий блок, как будто инициализированный<{m}>.

Throws:

Всякое исключение бросается призывом к<m.lock()>.

template <typename Lock>
nested_strict_lock<Lock> make_nested_strict_lock(Lock& lk); // EXTENSION

Returns:

a nested_strict_lock, как если бы он был инициализирован<{lk}>.

Throws:

Любое исключение, вызванное призывом<lk.lock()>.

// #include <boost/thread/synchroniezd_value.hpp> 
// #include <boost/thread/strict_lock_ptr.hpp> 
namespace boost
{
  template<typename T, typename Lockable = mutex>
  class strict_lock_ptr;
  template<typename T, typename Lockable = mutex>
  class const_strict_lock_ptr;
}
// #include <boost/thread/synchroniezd_value.hpp> 
// #include <boost/thread/strict_lock_ptr.hpp> 
template <typename T, typename Lockable = mutex>
class const_strict_lock_ptr
{
public:
  typedef T value_type;
  typedef Lockable mutex_type;
  const_strict_lock_ptr(const_strict_lock_ptr const& m_) = delete;
  const_strict_lock_ptr& operator=(const_strict_lock_ptr const& m_) = delete;
  const_strict_lock_ptr(T const& val, Lockable & mtx);
  const_strict_lock_ptr(T const& val, Lockable & mtx, adopt_lock_t tag);
  ~const_strict_lock_ptr();
  const T* operator->() const;
  const T& operator*() const;
};
const_strict_lock_ptr(T const& val, Lockable & m);

Effects:

Призывает<m.lock()>, сохраняет ссылку на него и на тип значения<val>.

Throws:

Всякое исключение бросается призывом к<m.lock()>.

const_strict_lock_ptr(T const& val, Lockable & m, adopt_lock_t tag);

Effects:

Сохраняется ссылка на него и на тип значения<val>.

Throws:

Ничего.

~const_strict_lock_ptr();

Effects:

<m.unlock()>на<Lockable>объект передан конструктору.

Throws:

Ничего.

const T* operator->() const;

Return:

возвращает постоянный указатель на защищенное значение.

Throws:

Ничего.

const T& operator*() const;

Return:

возвращает постоянную ссылку на охраняемую стоимость.

Throws:

Ничего.

// #include <boost/thread/synchroniezd_value.hpp> 
// #include <boost/thread/strict_lock_ptr.hpp> 
template <typename T, typename Lockable = mutex>
class strict_lock_ptr : public const_strict_lock_ptr<T,Lockable>
{
public:
  strict_lock_ptr(strict_lock_ptr const& m_) = delete;
  strict_lock_ptr& operator=(strict_lock_ptr const& m_) = delete;
  strict_lock_ptr(T & val, Lockable & mtx);
  strict_lock_ptr(T & val, Lockable & mtx, adopt_lock_t tag);
  ~strict_lock_ptr();
  T* operator->();
  T& operator*();
};
strict_lock_ptr(T const& val, Lockable & m);

Effects:

Призывает<m.lock()>, сохраняет ссылку на него и на тип значения<val>.

Throws:

Всякое исключение бросается призывом к<m.lock()>.

strict_lock_ptr(T const& val, Lockable & m, adopt_lock_t tag);

Effects:

Сохраняется ссылка на него и на тип значения<val>.

Throws:

Ничего.

~ strict_lock_ptr();

Effects:

<m.unlock()>на<Lockable>объект передан конструктору.

Throws:

Ничего.

T* operator->();

Return:

Верните указатель на защищенную ценность.

Throws:

Ничего.

T& operator*();

Return:

вернуть ссылку на охраняемую стоимость.

Throws:

Ничего.

// #include <boost/thread/externally_locked.hpp>
template <class T, typename MutexType = boost::mutex>
class externally_locked;
template <class T, typename MutexType>
class externally_locked<T&, MutexType>;
template <typename T, typename MutexType>
void swap(externally_locked<T, MutexType> & lhs, externally_locked<T, MutexType> & rhs);
// #include <boost/thread/externally_locked.hpp>
template <class T, typename MutexType>
class externally_locked
{
  //BOOST_CONCEPT_ASSERT(( CopyConstructible<T> ));
  BOOST_CONCEPT_ASSERT(( BasicLockable<MutexType> ));
public:
  typedef MutexType mutex_type;
  externally_locked(mutex_type& mtx, const T& obj);
  externally_locked(mutex_type& mtx,T&& obj);
  explicit externally_locked(mutex_type& mtx);
  externally_locked(externally_locked const& rhs);
  externally_locked(externally_locked&& rhs);
  externally_locked& operator=(externally_locked const& rhs);
  externally_locked& operator=(externally_locked&& rhs);
  // observers
  T& get(strict_lock<mutex_type>& lk);
  const T& get(strict_lock<mutex_type>& lk) const;
  template <class Lock>
  T& get(nested_strict_lock<Lock>& lk);
  template <class Lock>
  const T& get(nested_strict_lock<Lock>& lk) const;
  template <class Lock>
  T& get(Lock& lk);
  template <class Lock>
  T const& get(Lock& lk) const;
 mutex_type* mutex() const noexcept;
  // modifiers
  void lock();
  void unlock();
  bool try_lock();
  void swap(externally_locked&);
};

<externally_locked>является моделью<Lockable>, она маскирует объект типа<T>и фактически обеспечивает полный доступ к этому объекту через функции получения и установки элементов при условии, что вы передаете ссылку на объект строгой блокировки.

Здесь описаны только особенности<Lockable>.

externally_locked(mutex_type& mtx, const T& obj);

Requires:

T является моделью CopyConstructible.

Effects:

Конструирует внешне заблокированный объект, копирующий замаскированный тип.

Throws:

Любое исключение, вызванное призывом<T(obj)>.

externally_locked(mutex_type& mtx,T&& obj);

Requires:

Т является моделью подвижного.

Effects:

Конструирует внешне заблокированный объект, перемещая замаскированный тип.

Throws:

Любое исключение, вызванное призывом<T(obj)>.

externally_locked(mutex_type& mtx);

Requires:

T является моделью DefaultConstructible.

Effects:

Конструирует внешне заблокированный объект по умолчанию, создавая скрытый тип.

Throws:

Любое исключение, вызванное призывом<T()>.

externally_locked(externally_locked&& rhs);

Requires:

Т является моделью подвижного.

Effects:

Move конструирует внешне заблокированный объект, перемещая замаскированный тип и копируя ссылку mutex.

Throws:

Любое исключение, вызванное призывом<T(T&&)>.

externally_locked(externally_locked& rhs);

Requires:

T является копируемой моделью.

Effects:

Копия конструирует внешне заблокированный объект, копируя скрытый тип и копируя ссылку mutex.

Throws:

Любое исключение, вызванное призывом<T(T&)>.

externally_locked& operator=(externally_locked&& rhs);

Requires:

Т является моделью подвижного.

Effects:

Move присваивает внешне заблокированный объект, перемещая скрытый тип и копируя ссылку mutex.

Throws:

Любое исключение, вызванное призывом<T::operator=(T&&)>.

externally_locked& operator=(externally_locked const& rhs);

Requires:

T является копируемой моделью.

Effects:

Копия присваивает внешне заблокированный объект путем копирования скрытого типа и копирования ссылки mutex.

Throws:

Любое исключение, вызванное призывом<T::operator=(T&)>.

T& get(strict_lock<mutex_type>& lk);
const T& get(strict_lock<mutex_type>& lk) const;

Requires:

Параметр<lk>должен блокировать ассоциированный мутекс.

Returns:

Ссылка на скрытый объект

Throws:

<lock_error>, если<BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED>определено и предварительные условия выполнения не выполнены.

template <class Lock>
T& get(nested_strict_lock<Lock>& lk);
template <class Lock>
const T& get(nested_strict_lock<Lock>& lk) const;

Requires:

<is_same<mutex_type, typenameLock::mutex_type>>и<lk>параметр должен блокировать ассоциированный мутекс.

Returns:

Ссылка на скрытый объект

Throws:

<lock_error>, если<BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED>определено и предварительные условия выполнения не выполнены.

template <class Lock>
T& get(Lock& lk);
template <class Lock>
T const& get(Lock& lk) const;

Requires:

<Lock>является моделью<StrictLock>,<is_same<mutex_type, typenameLock::mutex_type>>и<lk>параметр должен блокировать связанный мутекс.

Returns:

Ссылка на скрытый объект

Throws:

<lock_error>, если<BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED>определено и предварительные условия выполнения не выполнены.

// #include <boost/thread/externally_locked.hpp>
template <class T, typename MutexType>
class externally_locked<T&, MutexType>
{
  //BOOST_CONCEPT_ASSERT(( CopyConstructible<T> ));
  BOOST_CONCEPT_ASSERT(( BasicLockable<MutexType> ));
public:
  typedef MutexType mutex_type;
  externally_locked(mutex_type& mtx, T& obj);
  explicit externally_locked(mutex_type& mtx);
  externally_locked(externally_locked const& rhs) noexcept;
  externally_locked(externally_locked&& rhs) noexcept;
  externally_locked& operator=(externally_locked const& rhs) noexcept;
  externally_locked& operator=(externally_locked&& rhs) noexcept;
  // observers
  T& get(strict_lock<mutex_type>& lk);
  const T& get(strict_lock<mutex_type>& lk) const;
  template <class Lock>
  T& get(nested_strict_lock<Lock>& lk);
  template <class Lock>
  const T& get(nested_strict_lock<Lock>& lk) const;
  template <class Lock>
  T& get(Lock& lk);
  template <class Lock>
  T const& get(Lock& lk) const;
 mutex_type* mutex() const noexcept;
  // modifiers
  void lock();
  void unlock();
  bool try_lock();
  void swap(externally_locked&) noexcept;
};

<externally_locked>является моделью<Lockable>, она маскирует объект типа<T>и фактически обеспечивает полный доступ к этому объекту через функции получения и установки элементов при условии, что вы передаете ссылку на объект строгой блокировки.

Здесь описаны только особенности<Lockable>.

externally_locked<T&>(mutex_type& mtx, T& obj) noexcept;

Effects:

Конструирует внешне заблокированный объект, копирующий скрытую ссылку.

externally_locked(externally_locked&& rhs) noexcept;

Effects:

Перемещает внешне заблокированный объект, перемещая замаскированный тип и копируя ссылку mutex

externally_locked& operator=(externally_locked&& rhs);

Effects:

Move присваивает внешне заблокированный объект, копируя скрытую ссылку и копируя ссылку mutex.

externally_locked& operator=(externally_locked const& rhs);

Requires:

T является копируемой моделью.

Effects:

Копия присваивает внешне заблокированный объект, копируя скрытую ссылку и копируя ссылку mutex.

Throws:

Любое исключение, вызванное призывом<T::operator=(T&)>.

T& get(strict_lock<mutex_type>& lk);
const T& get(strict_lock<mutex_type>& lk) const;

Requires:

Параметр<lk>должен блокировать ассоциированный мутекс.

Returns:

Ссылка на скрытый объект

Throws:

<lock_error>, если<BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED>определено и предварительные условия выполнения не выполнены.

template <class Lock>
T& get(nested_strict_lock<Lock>& lk);
template <class Lock>
const T& get(nested_strict_lock<Lock>& lk) const;

Requires:

<is_same<mutex_type, typenameLock::mutex_type>>и<lk>параметр должен блокировать ассоциированный мутекс.

Returns:

Ссылка на скрытый объект

Throws:

<lock_error>, если<BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED>определено и предварительные условия выполнения не выполнены.

template <class Lock>
T& get(Lock& lk);
template <class Lock>
T const& get(Lock& lk) const;

Requires:

<Lock>является моделью<StrictLock>,<is_same<mutex_type, typenameLock::mutex_type>>и<lk>параметр должен блокировать связанный мутекс.

Returns:

Ссылка на скрытый объект

Throws:

<lock_error>, если<BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED>определено и предварительные условия выполнения не выполнены.

template <typename T, typename MutexType>
void swap(externally_locked<T, MutexType> & lhs, externally_locked<T, MutexType> & rhs)
// #include <boost/thread/shared_lock_guard.hpp>
namespace boost
{
  template<typename SharedLockable>
  class shared_lock_guard
  {
  public:
      shared_lock_guard(shared_lock_guard const&) = delete;
      shared_lock_guard& operator=(shared_lock_guard const&) = delete;
      explicit shared_lock_guard(SharedLockable& m_);
      shared_lock_guard(SharedLockable& m_,boost::adopt_lock_t);
      ~shared_lock_guard();
  };
}

<shared_lock_guard>очень прост: на строительстве он приобретает долевое владение реализацией<SharedLockable>концепции, поставляемой в качестве конструктивного параметра. При разрушении собственность освобождается. Это обеспечивает простую блокировку объекта<SharedLockable>в стиле RAII для облегчения совместной блокировки и разблокировки. Кроме того,<shared_lock_guard>SharedLockable&m,boost::adopt_lock_tКонструктор позволяет объекту<shared_lock_guard>взять в совместное владение замок, уже удерживаемый текущей нитью.

Effects:

Сохраняется ссылка на<m>.Взывает<m.lock_shared()>[].

Throws:

Любое исключение, вызванное призывом<m.lock_shared()>[].

Precondition:

Нынешняя нить имеет замок на<m>, эквивалентный тому, который получен вызовом<m.lock_shared()>[].

Effects:

Содержит ссылку на<m>. Приобретает право собственности на состояние замка<m>.

Throws:

Ничего.

Effects:

Взывает<m.unlock_shared()>[]на<SharedLockable>объект, переданный конструктору.

Throws:

Ничего.

// #include <boost/thread/reverse_lock.hpp>
namespace boost
{
  template<typename Lock>
  class reverse_lock
  {
  public:
      reverse_lock(reverse_lock const&) = delete;
      reverse_lock& operator=(reverse_lock const&) = delete;
      explicit reverse_lock(Lock& m_);
      ~reverse_lock();
  };
}

<reverse_lock>обратить вспять работу замка: он предусматривает RAII-стиль, который разблокирует замок во время строительства и запирает его во время разрушения. Кроме того, он временно передает право собственности, так что mutex не может быть заблокирован с помощью блокировки.

Пример<reverse_lock>не владеетзамком никогда.

Effects:

Сохраняет ссылку на<m>. Призывает<m.unlock>(), если<m>владеет своим замком, а затем хранит mutex, позвонив<m.release()>.

Postcondition:

<!m.owns_lock()>[]&&mmutex[] [] ==0.

Throws:

Любое исключение, вызванное призывом<m.unlock>[].

Effects:

Пусть mtx хранится mutex*. Если 0 не вызывает<mtx->lock>[]и снова дает<mtx><Lock>, используя<adopt_lock_t>перегрузку.

Throws:

Исключение составляет<mtx->lock>[].

Remarks:

Обратите внимание, что если<mtx->lock>()бросит исключение при раскручивании программы, то не используйте reverse_lock, если исключение может быть брошено.

// #include <boost/thread/locks.hpp>
// #include <boost/thread/lock_algorithms.hpp>
namespace boost
{
  template<typename Lockable1,typename Lockable2>
  void lock(Lockable1& l1,Lockable2& l2);
  template<typename Lockable1,typename Lockable2,typename Lockable3>
  void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3);
  template<typename Lockable1,typename Lockable2,typename Lockable3,typename Lockable4>
  void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4);
  template<typename Lockable1,typename Lockable2,typename Lockable3,typename Lockable4,typename Lockable5>
  void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4,Lockable5& l5);
}

Effects:

Запирает<Lockable>объекты представлены в качестве аргументов в неопределенном и неопределенном порядке таким образом, чтобы избежать тупика. Безопасно называть эту функцию одновременно из нескольких потоков с одинаковыми мутексами (или другими блокируемыми объектами) в разных порядках без риска тупика. Если какая-либо из<lock()>или<try_lock()>операций на поставляемых<Lockable>объектах бросает исключение, любые замки, приобретенные функцией, будут выпущены до выхода функции.

Throws:

Любые исключения, вызванные вызовом<lock()>или<try_lock()>на поставленные<Lockable>объекты.

Postcondition:

Все поставляемые<Lockable>объекты заперты вызывающей нитью.

template<typename ForwardIterator>
void lock(ForwardIterator begin,ForwardIterator end);

Preconditions:

The value_type of ForwardIterator must implement the Lockable concept

Effects:

Закрывает все объекты<Lockable>в поставляемом диапазоне в неопределенном и неопределенном порядке таким образом, чтобы избежать тупика. Безопасно называть эту функцию одновременно из нескольких потоков с одинаковыми мутексами (или другими блокируемыми объектами) в разных порядках без риска тупика. Если какая-либо из операций<lock()>или<try_lock()>на объектах<Lockable>в поставляемом диапазоне выбрасывает исключение, любые замки, приобретенные функцией, будут выпущены до выхода функции.

Throws:

Любые исключения, вызванные вызовом<lock()>или<try_lock()>на поставленные<Lockable>объекты.

Postcondition:

Все объекты<Lockable>в поставляемом диапазоне заперты вызывающей нитью.

template<typename Lockable1,typename Lockable2>
int try_lock(Lockable1& l1,Lockable2& l2);
template<typename Lockable1,typename Lockable2,typename Lockable3>
int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3);
template<typename Lockable1,typename Lockable2,typename Lockable3,typename Lockable4>
int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4);
template<typename Lockable1,typename Lockable2,typename Lockable3,typename Lockable4,typename Lockable5>
int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4,Lockable5& l5);

Effects:

Звонки<try_lock()>по каждому из объектов<Lockable>приводятся в качестве аргументов. Если кто-либо из звонков<try_lock()>возвращает<false>, то все приобретенные замки отпускаются и возвращается нулевой индекс неудавшегося замка.

Если какая-либо из операций<try_lock()>на поставляемых<Lockable>объектах бросает исключение, любые замки, приобретенные функцией, будут выпущены до выхода функции.

Returns:

-1 if all the supplied Lockable objects are now locked by the calling thread, the zero-based index of the object which could not be locked otherwise.

Throws:

Любые исключения, вызванные вызовом<try_lock()>на поставленные<Lockable>объекты.

Postcondition:

Если функция возвращается<-1>, все поставляемые<Lockable>объекты запираются вызывающей нитью. В противном случае любые блокировки, приобретенные этой функцией, будут освобождены.

template<typename ForwardIterator>
ForwardIterator try_lock(ForwardIterator begin,ForwardIterator end);

Preconditions:

The value_type of ForwardIterator must implement the Lockable concept

Effects:

Звонки<try_lock()>по каждому из<Lockable>объектов в поставляемом диапазоне. Если какой-либо из звонков в<try_lock()>возвращается<false>, то все приобретенные замки отпускаются и возвращается итератор, ссылающийся на неудавшийся замок.

Если какая-либо из операций<try_lock()>на поставляемых<Lockable>объектах бросает исключение, любые замки, приобретенные функцией, будут выпущены до выхода функции.

Returns:

end if all the supplied Lockable objects are now locked by the calling thread, an iterator referencing the object which could not be locked otherwise.

Throws:

Любые исключения, вызванные вызовом<try_lock()>на поставленные<Lockable>объекты.

Postcondition:

Если функция возвращается<end>, то все объекты<Lockable>в поставляемом диапазоне запираются вызывающей нитью, в противном случае все замки, приобретенные функцией, освобождаются.

namespace boost
{
  template <typename Lockable>
  unique_lock<Lockable> make_unique_lock(Lockable& mtx); // EXTENSION
  template <typename Lockable>
  unique_lock<Lockable> make_unique_lock(Lockable& mtx, adopt_lock_t); // EXTENSION
  template <typename Lockable>
  unique_lock<Lockable> make_unique_lock(Lockable& mtx, defer_lock_t); // EXTENSION
  template <typename Lockable>
  unique_lock<Lockable> make_unique_lock(Lockable& mtx, try_to_lock_t); // EXTENSION
#if ! defined(BOOST_THREAD_NO_MAKE_UNIQUE_LOCKS)
  template <typename ...Lockable>
  std::tuple<unique_lock<Lockable> ...> make_unique_locks(Lockable& ...mtx); // EXTENSION
#endif
}
template <typename Lockable>
unique_lock<Lockable> make_unique_lock(Lockable& mtx); // EXTENSION

Returns:

a<boost::unique_lock>, как если бы они были инициализированы с<unique_lock<Lockable>(mtx)>.

Throws:

Любое исключение, вызванное призывом<boost::unique_lock><Lockable>mtx.

template <typename Lockable>
unique_lock<Lockable> make_unique_lock(Lockable& mtx, adopt_lock_t tag); // EXTENSION
template <typename Lockable>
unique_lock<Lockable> make_unique_lock(Lockable& mtx, defer_lock_t tag); // EXTENSION
template <typename Lockable>
unique_lock<Lockable> make_unique_lock(Lockable& mtx, try_to_lock_t tag); // EXTENSION

Returns:

a<boost::unique_lock>, как если бы они были инициализированы<unique_lock<Lockable>(mtx,tag)>.

Throws:

Any exception thrown by the call to boost::unique_lock<Lockable>(mtx,tag).

template <typename ...Lockable>
std::tuple<unique_lock<Lockable> ...> make_unique_locks(Lockable& ...mtx); // EXTENSION

Effect:

Закрывает все мутексы.

Returns:

a std::tuple of unique<boost::unique_lock>, владеющий каждым из мутексов.

Throws:

Исключение сделано<boost::lock(mtx...)>.

#include <boost/thread/mutex.hpp>
class mutex:
    boost::noncopyable
{
public:
    mutex();
    ~mutex();
    void lock();
    bool try_lock();
    void unlock();
    typedef platform-specific-type native_handle_type;
    native_handle_type native_handle();
    typedef unique_lock<mutex> scoped_lock;
    typedef unspecified-type scoped_try_lock;
};

<boost::mutex>реализует концепцию<Lockable>для обеспечения исключительного владения mutex. В большинстве случаев одна нить может владеть замком на данном экземпляре<boost::mutex>в любое время. Разрешаются множественные параллельные призывы к<lock()>,<try_lock()>и<unlock()>.

typedef platform-specific-type native_handle_type;
native_handle_type native_handle();

Effects:

Возвращает пример<native_handle_type>, который можно использовать с API-интерфейсами для управления базовой реализацией. Если таких случаев нет, то<native_handle()>и<native_handle_type>отсутствуют.

Throws:

Ничего.

#include <boost/thread/mutex.hpp>
typedef mutex try_mutex;

<boost::try_mutex>- это<typedef>до<boost::mutex>, предусмотренная обратная совместимость с предыдущими выпусками повышения.

#include <boost/thread/mutex.hpp>
class timed_mutex:
    boost::noncopyable
{
public:
    timed_mutex();
    ~timed_mutex();
    void lock();
    void unlock();
    bool try_lock();
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& t);
    typedef platform-specific-type native_handle_type;
    native_handle_type native_handle();
    typedef unique_lock<timed_mutex> scoped_timed_lock;
    typedef unspecified-type scoped_try_lock;
    typedef scoped_timed_lock scoped_lock;
#if defined BOOST_THREAD_PROVIDES_DATE_TIME || defined BOOST_THREAD_DONT_USE_CHRONO
    bool timed_lock(system_time const & abs_time);
    template<typename TimeDuration>
    bool timed_lock(TimeDuration const & relative_time);
#endif
};

<boost::timed_mutex>реализует концепцию<TimedLockable>для обеспечения исключительного владения mutex. В большинстве случаев одна нить может владеть замком на данном экземпляре<boost::timed_mutex>в любое время. Допускаются множественные одновременные звонки на<lock()>,<try_lock()>,<timed_lock()>,<timed_lock()>и<unlock()>.

typedef platform-specific-type native_handle_type;
native_handle_type native_handle();

Effects:

Возвращает пример<native_handle_type>, который можно использовать с API-интерфейсами для управления базовой реализацией. Если таких случаев нет, то<native_handle()>и<native_handle_type>отсутствуют.

Throws:

Ничего.

#include <boost/thread/recursive_mutex.hpp>
class recursive_mutex:
    boost::noncopyable
{
public:
    recursive_mutex();
    ~recursive_mutex();
    void lock();
    bool try_lock() noexcept;
    void unlock();
    typedef platform-specific-type native_handle_type;
    native_handle_type native_handle();
    typedef unique_lock<recursive_mutex> scoped_lock;
    typedef unspecified-type scoped_try_lock;
};

<boost::recursive_mutex>реализует концепцию<Lockable>для обеспечения рекурсивного мутекса исключительного владения. В большинстве случаев одна нить может владеть замком на данном экземпляре<boost::recursive_mutex>в любое время. Разрешаются множественные одновременные звонки на<lock()>,<try_lock()>и<unlock()>. Нить, которая уже имеет исключительное право собственности на данный экземпляр<boost::recursive_mutex>, может вызывать<lock()>или<try_lock()>для приобретения дополнительного уровня владения mutex.<unlock()>должен быть вызван один раз для каждого уровня владения, приобретенного одной нитью, прежде чем собственность может быть приобретена другой нитью.

typedef platform-specific-type native_handle_type;
native_handle_type native_handle();

Effects:

Возвращает пример<native_handle_type>, который можно использовать с API-интерфейсами для управления базовой реализацией. Если таких случаев нет, то<native_handle()>и<native_handle_type>отсутствуют.

Throws:

Ничего.

#include <boost/thread/recursive_mutex.hpp>
typedef recursive_mutex recursive_try_mutex;

<boost::recursive_try_mutex>- это<typedef>до<boost::recursive_mutex>, предусмотренная для обратной совместимости с предыдущими выпусками импульса.

#include <boost/thread/recursive_mutex.hpp>
class recursive_timed_mutex:
    boost::noncopyable
{
public:
    recursive_timed_mutex();
    ~recursive_timed_mutex();
    void lock();
    bool try_lock() noexcept;
    void unlock();
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& t);
    typedef platform-specific-type native_handle_type;
    native_handle_type native_handle();
    typedef unique_lock<recursive_timed_mutex> scoped_lock;
    typedef unspecified-type scoped_try_lock;
    typedef scoped_lock scoped_timed_lock;
#if defined BOOST_THREAD_PROVIDES_DATE_TIME || defined BOOST_THREAD_DONT_USE_CHRONO
    bool timed_lock(system_time const & abs_time);
    template<typename TimeDuration>
    bool timed_lock(TimeDuration const & relative_time);
#endif
};

<boost::recursive_timed_mutex>реализует концепцию<TimedLockable>для обеспечения рекурсивного мутекса исключительного владения. В большинстве случаев одна нить может владеть замком на данном экземпляре<boost::recursive_timed_mutex>в любое время. Допускаются множественные одновременные звонки на<lock()>,<try_lock()>,<timed_lock()>,<timed_lock()>и<unlock()>. Нить, которая уже имеет исключительное право собственности на данный экземпляр<boost::recursive_timed_mutex>, может вызывать<lock()>,<timed_lock()>,<timed_lock()>или<try_lock()>для приобретения дополнительного уровня владения mutex.<unlock()>должен быть вызван один раз для каждого уровня владения, приобретенного одной нитью, прежде чем собственность может быть приобретена другой нитью.

typedef platform-specific-type native_handle_type;
native_handle_type native_handle();

Effects:

Возвращает пример<native_handle_type>, который можно использовать с API-интерфейсами для управления базовой реализацией. Если таких случаев нет, то<native_handle()>и<native_handle_type>отсутствуют.

Throws:

Ничего.

#include <boost/thread/shared_mutex.hpp>
class shared_mutex
{
public:
    shared_mutex(shared_mutex const&) = delete;
    shared_mutex& operator=(shared_mutex const&) = delete;
    shared_mutex();
    ~shared_mutex();
    void lock_shared();
    bool try_lock_shared();
    template <class Rep, class Period>
    bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock_shared();
    void lock();
    bool try_lock();
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();
#if defined BOOST_THREAD_PROVIDES_DEPRECATED_FEATURES_SINCE_V3_0_0
    // use upgrade_mutex instead.
    void lock_upgrade(); // EXTENSION
    void unlock_upgrade(); // EXTENSION
    void unlock_upgrade_and_lock(); // EXTENSION
    void unlock_and_lock_upgrade(); // EXTENSION
    void unlock_and_lock_shared(); // EXTENSION
    void unlock_upgrade_and_lock_shared(); // EXTENSION
#endif
#if defined BOOST_THREAD_USES_DATETIME
    bool timed_lock_shared(system_time const& timeout); // DEPRECATED
    bool timed_lock(system_time const& timeout); // DEPRECATED
#endif
};

Класс<boost::shared_mutex>обеспечивает реализацию многочитателя/однописателя mutex. Он реализует концепцию<SharedLockable>.

Допускаются множественные одновременные звонки на<lock()>,<try_lock()>,<try_lock_for>(),<try_lock_until>(),<timed_lock()>,<lock_shared()>,<try_lock_shared_for>,<try_lock_shared_until>,<try_lock_shared()>и<timed_lock_shared()>.

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

#include <boost/thread/shared_mutex.hpp>
class upgrade_mutex
{
public:
    upgrade_mutex(upgrade_mutex const&) = delete;
    upgrade_mutex& operator=(upgrade_mutex const&) = delete;
    upgrade_mutex();
    ~upgrade_mutex();
    void lock_shared();
    bool try_lock_shared();
    template <class Rep, class Period>
    bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock_shared();
    void lock();
    bool try_lock();
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();
    void lock_upgrade();
    template <class Rep, class Period>
    bool try_lock_upgrade_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_upgrade_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock_upgrade();
    // Shared <-> Exclusive
#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
    bool try_unlock_shared_and_lock();
    template <class Rep, class Period>
    bool try_unlock_shared_and_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_unlock_shared_and_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
#endif
    void unlock_and_lock_shared();
    // Shared <-> Upgrade
#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
    bool try_unlock_shared_and_lock_upgrade();
    template <class Rep, class Period>
    bool try_unlock_shared_and_lock_upgrade_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_unlock_shared_and_lock_upgrade_until(const chrono::time_point<Clock, Duration>& abs_time);
#endif
    void unlock_upgrade_and_lock_shared();
    // Upgrade <-> Exclusive
    void unlock_upgrade_and_lock();
#if    defined(BOOST_THREAD_PLATFORM_PTHREAD)
    || defined(BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN)
    bool try_unlock_upgrade_and_lock();
    template <class Rep, class Period>
    bool try_unlock_upgrade_and_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_unlock_upgrade_and_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
#endif
    void unlock_and_lock_upgrade();
};

Класс<boost::upgrade_mutex>обеспечивает реализацию многочитателя/однописателя mutex. Он реализует концепцию<UpgradeLockable>.

Допускаются множественные одновременные звонки на<lock()>,<try_lock()>,<try_lock_for>(),<try_lock_until>(),<timed_lock()>,<lock_shared()>,<try_lock_shared_for>,<try_lock_shared_until>,<try_lock_shared()>и<timed_lock_shared()>.

#include <boost/thread/null_mutex.hpp>
class null_mutex
{
public:
    null_mutex(null_mutex const&) = delete;
    null_mutex& operator=(null_mutex const&) = delete;
    null_mutex();
    ~null_mutex();
    void lock_shared();
    bool try_lock_shared();
 #ifdef BOOST_THREAD_USES_CHRONO
    template <class Rep, class Period>
    bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
 #endif
    void unlock_shared();
    void lock();
    bool try_lock();
 #ifdef BOOST_THREAD_USES_CHRONO
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
 #endif
    void unlock();
    void lock_upgrade();
 #ifdef BOOST_THREAD_USES_CHRONO
    template <class Rep, class Period>
    bool try_lock_upgrade_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_lock_upgrade_until(const chrono::time_point<Clock, Duration>& abs_time);
 #endif
    void unlock_upgrade();
    // Shared <-> Exclusive
    bool try_unlock_shared_and_lock();
 #ifdef BOOST_THREAD_USES_CHRONO
    template <class Rep, class Period>
    bool try_unlock_shared_and_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_unlock_shared_and_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
 #endif
    void unlock_and_lock_shared();
    // Shared <-> Upgrade
    bool try_unlock_shared_and_lock_upgrade();
 #ifdef BOOST_THREAD_USES_CHRONO
    template <class Rep, class Period>
    bool try_unlock_shared_and_lock_upgrade_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_unlock_shared_and_lock_upgrade_until(const chrono::time_point<Clock, Duration>& abs_time);
 #endif
    void unlock_upgrade_and_lock_shared();
    // Upgrade <-> Exclusive
    void unlock_upgrade_and_lock();
    bool try_unlock_upgrade_and_lock();
 #ifdef BOOST_THREAD_USES_CHRONO
    template <class Rep, class Period>
    bool try_unlock_upgrade_and_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
    bool try_unlock_upgrade_and_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
 #endif
    void unlock_and_lock_upgrade();
};

Класс<boost::null_mutex>обеспечивает безоперационную реализацию многочитателя/однописателя mutex. Это модель концепции<UpgradeLockable>.

Synopsis
namespace boost
{
  enum class cv_status;
  {
    no_timeout,
    timeout
  };
  class condition_variable;
  class condition_variable_any;
  void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
}

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

boost::condition_variable cond;
boost::mutex mut;
bool data_ready;
void process_data();
void wait_for_data_to_process()
{
    boost::unique_lock<boost::mutex> lock(mut);
    while(!data_ready)
    {
        cond.wait(lock);
    }
    process_data();
}

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

В среднем, другая нить устанавливает условие<true>, а затем вызывает либо<notify_one>, либо<notify_all>на переменной условия, чтобы разбудить одну ожидающую нить или все ожидающие нити соответственно.

void retrieve_data();
void prepare_data();
void prepare_data_for_processing()
{
    retrieve_data();
    prepare_data();
    {
        boost::lock_guard<boost::mutex> lock(mut);
        data_ready=true;
    }
    cond.notify_one();
}

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

Этот пример использует объект типа<condition_variable>, но будет работать так же хорошо с объектом типа<condition_variable_any>:<condition_variable_any>является более общим и будет работать с любым типом замка или мутекса, тогда как<condition_variable>требует, чтобы замок, переданный<wait>, был экземпляром<boost::unique_lock<boost::mutex>>. Это позволяет<condition_variable>в некоторых случаях делать оптимизации, основанные на знании типа мутекса;<condition_variable_any>обычно имеет более сложную реализацию, чем<condition_variable>.

//#include <boost/thread/condition_variable.hpp>
namespace boost
{
    class condition_variable
    {
    public:
        condition_variable();
        ~condition_variable();
        void notify_one() noexcept;
        void notify_all() noexcept;
        void wait(boost::unique_lock<boost::mutex>& lock);
        template<typename predicate_type>
        void wait(boost::unique_lock<boost::mutex>& lock,predicate_type predicate);
        template <class Clock, class Duration>
        typename cv_status::type
        wait_until(
            unique_lock<mutex>& lock,
            const chrono::time_point<Clock, Duration>& t);
        template <class Clock, class Duration, class Predicate>
        bool
        wait_until(
            unique_lock<mutex>& lock,
            const chrono::time_point<Clock, Duration>& t,
            Predicate pred);
        template <class Rep, class Period>
        typename cv_status::type
        wait_for(
            unique_lock<mutex>& lock,
            const chrono::duration<Rep, Period>& d);
        template <class Rep, class Period, class Predicate>
        bool
        wait_for(
            unique_lock<mutex>& lock,
            const chrono::duration<Rep, Period>& d,
            Predicate pred);
    #if defined BOOST_THREAD_USES_DATETIME
        bool timed_wait(boost::unique_lock<boost::mutex>& lock,boost::system_time const& abs_time);
        template<typename duration_type>
        bool timed_wait(boost::unique_lock<boost::mutex>& lock,duration_type const& rel_time);
        template<typename predicate_type>
        bool timed_wait(boost::unique_lock<boost::mutex>& lock,boost::system_time const& abs_time,predicate_type predicate);
        template<typename duration_type,typename predicate_type>
        bool timed_wait(boost::unique_lock<boost::mutex>& lock,duration_type const& rel_time,predicate_type predicate);
        bool timed_wait(boost::unique_lock<boost::mutex>& lock,boost::xtime const& abs_time);
        template<typename predicate_type>
        bool timed_wait(boost::unique_lock<boost::mutex>& lock,boost::xtime const& abs_time,predicate_type predicate);
    #endif
    };
}

Effects:

Построение объекта класса<condition_variable>.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

Precondition:

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

Effects:

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

Throws:

Ничего.

Effects:

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

Throws:

Ничего.

Effects:

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

Throws:

Ничего.

Precondition:

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

Effects:

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

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Precondition:

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

Effects:

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

Returns:

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

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Precondition:

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

Effects:

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

Returns:

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

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

[Note]Note

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

Effects:

если

while(!pred())
{
    if(!timed_wait(lock,abs_time))
    {
        return pred();
    }
}
return true;

Precondition:

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

Effects:

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

Returns:

<cv_status::timeout>если звонок возвращается, потому что время, указанное<abs_time>, было достигнуто,<cv_status::no_timeout>иначе.

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Precondition:

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

Effects:

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

Returns:

<cv_status::timeout>если звонок возвращается, поскольку период времени, указанный в<rel_time>, истек,<cv_status::no_timeout >в противном случае.

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

[Note]Note

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

//#include <boost/thread/condition_variable.hpp>
namespace boost
{
    class condition_variable_any
    {
    public:
        condition_variable_any();
        ~condition_variable_any();
        void notify_one();
        void notify_all();
        template<typename lock_type>
        void wait(lock_type& lock);
        template<typename lock_type,typename predicate_type>
        void wait(lock_type& lock,predicate_type predicate);
        template <class lock_type, class Clock, class Duration>
        cv_status wait_until(
            lock_type& lock,
            const chrono::time_point<Clock, Duration>& t);
        template <class lock_type, class Clock, class Duration, class Predicate>
        bool wait_until(
            lock_type& lock,
            const chrono::time_point<Clock, Duration>& t,
            Predicate pred);
        template <class lock_type, class Rep, class Period>
        cv_status wait_for(
            lock_type& lock,
            const chrono::duration<Rep, Period>& d);
        template <class lock_type, class Rep, class Period, class Predicate>
        bool wait_for(
            lock_type& lock,
            const chrono::duration<Rep, Period>& d,
            Predicate pred);
    #if defined BOOST_THREAD_USES_DATETIME
        template<typename lock_type>
        bool timed_wait(lock_type& lock,boost::system_time const& abs_time);
        template<typename lock_type,typename duration_type>
        bool timed_wait(lock_type& lock,duration_type const& rel_time);
        template<typename lock_type,typename predicate_type>
        bool timed_wait(lock_type& lock,boost::system_time const& abs_time,predicate_type predicate);
        template<typename lock_type,typename duration_type,typename predicate_type>
        bool timed_wait(lock_type& lock,duration_type const& rel_time,predicate_type predicate);
        template<typename lock_type>
        bool timed_wait(lock_type>& lock,boost::xtime const& abs_time);
        template<typename lock_type,typename predicate_type>
        bool timed_wait(lock_type& lock,boost::xtime const& abs_time,predicate_type predicate);
    #endif
    };
}

Effects:

Построение объекта класса<condition_variable_any>.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

Precondition:

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

Effects:

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

Throws:

Ничего.

Effects:

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

Throws:

Ничего.

Effects:

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

Throws:

Ничего.

Effects:

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

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Effects:

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

Returns:

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

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Effects:

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

Returns:

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

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

[Note]Note

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

Effects:

если

while(!pred())
{
    if(!timed_wait(lock,abs_time))
    {
        return pred();
    }
}
return true;

Effects:

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

Returns:

<cv_status::timeout>если звонок возвращается, потому что время, указанное<abs_time>, было достигнуто,<cv_status::no_timeout>иначе.

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Effects:

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

Returns:

<cv_status::timeout>если звонок возвращается, потому что время, указанное<abs_time>, было достигнуто,<cv_status::no_timeout>иначе.

Postcondition:

<lock>запирается текущей нитью.

Throws:

<boost::thread_resource_error>Если произошла ошибка.<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

[Note]Note

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

// #include <boost/thread/condition.hpp>
namespace boost
{
  typedef condition_variable_any condition;
}

Typedef<condition>обеспечивает обратную совместимость с предыдущими релизами.

// #include <boost/thread/condition_variable.hpp>
namespace boost
{
  void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
}

Requires:

lk is locked by the calling thread and either no other thread is waiting on cond, or lk.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_for, or wait_until) threads.

Effects:

передает право собственности на блокировку, связанную с<lk>, во внутреннее хранилище и графики<cond>, которые должны быть уведомлены при выходе текущей резьбы, после того как все объекты длительности хранения резьбы, связанные с текущей резьбой, были уничтожены. Это уведомление должно быть таким, как если бы

lk.unlock();
cond.notify_all();

#include <boost/thread/once.hpp>
namespace boost
{
  struct once_flag;
  template<typename Function, class ...ArgTypes>
  inline void call_once(once_flag& flag, Function&& f, ArgTypes&&... args);
#if defined BOOST_THREAD_PROVIDES_DEPRECATED_FEATURES_SINCE_V3_0_0
  void call_once(void (*func)(),once_flag& flag);
#endif
}
[Warning]Warning

Вариадный прототип предоставляется только на компиляторах C++11, поддерживающих вариадные шаблоны, в противном случае интерфейс ограничен до 3 параметров.

[Warning]Warning

Семантика движения обеспечивается только на компиляторах C++11, поддерживающих SFINAE-экспрессию, decltype N3276 и auto. В ожидании повышения::bind, который движется осознанно.

<boost::call_once>обеспечивает механизм для обеспечения того, чтобы процедура инициализации выполнялась ровно один раз без гонок данных или тупиков.

#ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11
struct once_flag
{
  constexprr once_flag() noexcept;
  once_flag(const once_flag&) = delete;
  once_flag& operator=(const once_flag&) = delete;
};
#else
typedef platform-specific-type once_flag;
#define BOOST_ONCE_INIT platform-specific-initializer
#endif

Объекты типа<boost::once_flag>должны быть инициализированы с<BOOST_ONCE_INIT>, если BOOST_THREAD_PROVIDES_ONCE_CXX11 не определен

boost::once_flag f=BOOST_ONCE_INIT;
template<typename Function, class ...ArgTypes>
inline void call_once(once_flag& flag, Function&& f, ArgTypes&&... args);

Requires:

Function and each or the ArgTypes are MoveConstructible and invoke(decay_copy(boost::forward<Function>(f)), decay_copy(boost::forward<ArgTypes>(args))...) shall be well formed.

Effects:

Звонки на<call_once>на тот же<once_flag>объект сериализуются. Если не было никакого предварительного эффективного<call_once>на том же<once_flag>объекте, аргумент<func>называется как-если путем вызова<invoke(decay_copy(boost::forward<Function>(f)),decay_copy(boost::forward<ArgTypes>(args))...)>, и вызов<call_once>эффективен, если и только если<invoke(decay_copy(boost::forward<Function>(f)), decay_copy(boost::forward<ArgTypes>(args))...)>возвращается без исключения. Если же выпадает исключение, то оно распространяется на абонента. Если на одном и том же<once_flag>объекте был ранее действительный<call_once>,<call_once>возвращается без вызова<func>.

Synchronization:

Завершение эффективного<call_once>вызова на<once_flag>объект синхронизируется со всеми последующими<call_once>вызовами на том же<once_flag>объекте.

Throws:

thread_resource_error when the effects cannot be achieved or any exception propagated from func.

Note:

Функция, переданная<call_once>, не должна также вызывать<call_once>прохождение того же<once_flag>объекта. Это может привести к тупику или вызову пройденной функции во второй раз. Альтернатива состоит в том, чтобы позволить второму вызову немедленно вернуться, но это предполагает, что код знает, что он был вызван рекурсивно, и может продолжаться, даже если вызов в<call_once>на самом деле не вызывал функцию, и в этом случае он также может избежать вызова<call_once>рекурсивно.

Note:

На некоторых компиляторах эта функция имеет некоторые ограничения, например, если вариадные шаблоны не поддерживаются, количество аргументов ограничено 3;

void call_once(void (*func)(),once_flag& flag);

Эта вторая перегрузка предусмотрена для обратной совместимости и обесценена.<call_once(func,flag)>— то же, что и<call_once(flag,func)>.

Барьер — это простая концепция. Также известный какрандеву, это точка синхронизации между несколькими потоками. Барьер сконфигурирован для определенного количества нитей (2544), и по мере того, как нити достигают барьера, они должны ждать, пока все нити<n>не появятся. После того, как<n>-я нить достигла барьера, все ожидающие нити могут продолжиться, и барьер сбрасывается.

#include <boost/thread/barrier.hpp>
class barrier
{
public:
    barrier(barrier const&) = delete;
    barrier& operator=(barrier const&) = delete;
    barrier(unsigned int count);
    template <typename F>
    barrier(unsigned int count, F&&);
    ~barrier();
    bool wait();
    void count_down_and_wait();
};

Случаи<boost::barrier>не являются копируемыми или подвижными.

barrier(unsigned int count);

Effects:

Постройте барьер для<count>нитей.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

barrier(unsigned int count, F&& completion);

Requires:

Тип результата вызова функции завершения<completion()>—<void>или<unsignedint>.

Effects:

Постройте барьер для<count>потоков и функцию завершения<completion>.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

~barrier();

Precondition:

Нити не ждут<*this>.

Effects:

Уничтожает<*this>.

Throws:

Ничего.

bool wait();

Effects:

Блокируйте до тех пор, пока<count>нити не вызовут<wait>или<count_down_and_wait>на<*this>. Когда нить<count>вызывает<wait>, барьер сбрасывается, и все ожидающие нити разблокируются. Сброс зависит от того, был ли барьер построен с функцией завершения или нет. Если функция завершения отсутствует или если результат функции завершения недействителен, сброс заключается в восстановлении исходного количества. В противном случае остальные состоят в присвоении результата функции завершения (который не должен быть 0).

Returns:

<true>ровно по одной нити из каждой партии ожидающих нитей,<false>иначе.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

-<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Notes:

<wait()>являетсяточкой прерывания.

void count_down_and_wait();

Effects:

Блокируйте до тех пор, пока<count>нити не вызовут<wait>или<count_down_and_wait>на<*this>. Когда нить<count>вызывает<wait>, барьер сбрасывается, и все ожидающие нити разблокируются. Сброс зависит от того, был ли барьер построен с функцией завершения или нет. Если функция завершения отсутствует или если результат функции завершения недействителен, сброс заключается в восстановлении исходного количества. В противном случае остальные состоят в присвоении результата функции завершения (который не должен быть 0).

Throws:

<boost::thread_resource_error>Если произошла ошибка.

-<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Notes:

<count_down_and_wait()>являетсяточкой прерывания.

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

Примеры использования защелки включают:

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

Примером первого варианта использования может служить следующее:

void DoWork(thread_pool* pool) {
  latch completion_latch(NTASKS);
  for (int i = 0; i < NTASKS; ++i) {
    pool->submit([&] {
      // perform work
      ...
      completion_latch.count_down();
    }));
  }
  // Block until work is done
  completion_latch.wait();
}

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

void DoWork() {
  latch start_latch(1);
  vector<thread*> workers;
  for (int i = 0; i < NTHREADS; ++i) {
    workers.push_back(new thread([&] {
      // Initialize data structures. This is CPU bound.
      ...
      start_latch.wait();
      // perform work
      ...
    }));
  }
  // Load input data. This is I/O bound.
  ...
  // Threads can now start processing
  start_latch.count_down();
  }
#include <boost/thread/latch.hpp>
class latch
{
public:
    latch(latch const&) = delete;
    latch& operator=(latch const&) = delete;
    latch(std::size_t count);
    ~latch();
    void wait();
    bool try_wait();
    template <class Rep, class Period>
    cv_status wait_for(const chrono::duration<Rep, Period>& rel_time);
    template <class lock_type, class Clock, class Duration>
    cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time);
    void count_down();
    void count_down_and_wait();
};

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

Случаи<latch>не являются копируемыми или подвижными.

latch(std::size_t count);

Effects:

Постройте защелку с начальным значением для внутреннего счетчика.

Note:

Счетчик может быть нулевым.

Throws:

Ничего.

~latch();

Precondition:

Нет нити, ожидающей или вызывающей обратный отсчет<*this>.

Effects:

Уничтожает защелку<*this>.

Throws:

Ничего.

void wait();

Effects:

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

Throws:

<boost::thread_resource_error>Если произошла ошибка.

-<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Notes:

<wait()>являетсяточкой прерывания.

bool try_wait();

Returns:

Возвращает истинное, если внутреннее число равно 0, и ложное в противном случае. Не блокирует нить вызова.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

template <class Rep, class Period>
cv_status wait_for(const chrono::duration<Rep, Period>& rel_time);

Effects:

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

Returns:

cv_status::no_timeout, если внутреннее число равно 0, и cv_status::timeout, если продолжительность истекла.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

-<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Notes:

<wait_for()>являетсяточкой прерывания.

template <class lock_type, class Clock, class Duration>
cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time);

Effects:

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

Returns:

cv_status::no_timeout, если внутреннее число равно 0, и cv_status::timeout, если точка time_point достигнута.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

-<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Notes:

<wait_until()>являетсяточкой прерывания.

void count_down();

Requires:

Внутренний счетчик не равен нулю.

Effects:

Уменьшает внутренний счет на 1 и возвращается. Если количество достигает 0, любые потоки, заблокированные в ожидании (), будут выпущены.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

-<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Notes:

<count_down()>являетсяточкой прерывания.

void count_down_and_wait();

Requires:

Внутренний счетчик не равен нулю.

Effects:

Уменьшает внутренний счет на 1. Если результирующее число не равно 0, блокирует вызывающую нить до тех пор, пока внутреннее число не будет уменьшено до 0 одним или несколькими другими потоками, вызывающими Count_down() или Count_down_and_wait().

Throws:

<boost::thread_resource_error>Если произошла ошибка.

-<boost::thread_interrupted>если ожидание прерывалось вызовом<interrupt()>на<boost::thread>объект, связанный с текущей нитью исполнения.

Notes:

<count_down_and_wait()>являетсяточкой прерывания.

[править]

reset( size_t );

Requires:

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

Returns:

Сбрасывает защелку с новым значением для начального подсчета потока.

Throws:

<boost::thread_resource_error>Если произошла ошибка.

]

[Warning]Warning

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

[Note]Note

Эти особенности основаны на. N3785 - Исполнители и планировщики пересмотр 3Предложение C++1y от Криса Майсена, Никласа Густафссона, Мэтта Остерна, Джеффри Яскина. Нижеследующий текст был адаптирован из этой статьи, чтобы показать различия.

Исполнители — это объекты, которые могут выполнять единицы работы, упакованные в качестве функциональных объектов. Повышаю. Нить отличается от N3785 главным образом тем, что Исполнителю не нужно наследовать от Исполнителя абстрактного класса. Статический полиморфизм используется вместо этого, а стирание типа используется внутри.

Многопоточные программы часто включают дискретные (иногда небольшие) блоки работы, которые выполняются асинхронно. Это часто включает в себя передачу рабочих блоков в какой-то компонент, который управляет выполнением. У нас уже есть импульс: асинхронный синтез, который потенциально выполняет функцию асинхронно и в конечном итоге возвращает ее результат в будущее. (“As if” запустив новый поток.)

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

  1. Бассейны
    1. Простой неограниченный пул потоков, который может стоять в очереди на неограниченное количество работы и поддерживает выделенный набор потоков (до некоторого максимума), которые выстраиваются в очередь и выполняют работу по мере ее доступности.
    2. Связанные резьбовые пулы, которые могут быть реализованы как специализация предыдущих с ограниченной очередью или семафором, что ограничивает количество очередей в попытке связать время ожидания выполнения и / или ограничить использование ресурсов для рабочих задач, которые удерживают состояние, которое дорого держать.
    3. Распылители ниток, в которых каждая работа всегда выполняется в новой нити.
    4. Приоритетные нитевые пулы, которые имеют работы, которые не одинаково приоритетны, так что работа может при необходимости переместиться в переднюю часть очереди исполнения. Для этого требуется специальный компаратор или функция приоритезации, позволяющая упорядочить работу и обычно реализуемая как блокирующая очередь приоритета перед бассейном вместо блокирующей очереди. Это имеет много применений, но имеет несколько специализированный характер и излишне загромождает первоначальный интерфейс.
    5. Работа по краже потоковых пулов, это специализированный вариант использования и инкапсулируется в ForkJoinPool в Java, что позволяет легковесной работе создаваться задачами в пуле и либо выполняться одной и той же нитью для повышения эффективности вызова, либо украденной другой нитью без дополнительной работы. Они остаются в стороне до тех пор, пока не появится более конкретное предложение о вилочном соединении или пока не возникнет более явная необходимость, поскольку их может быть сложно реализовать.
  2. Исполнители взаимного исключения
    1. Серийные исполнители, которые гарантируют выполнение всех работ таким образом, чтобы никакие две работы не выполнялись одновременно. Это позволяет ставить последовательность операций в очередь и поддерживать последовательный порядок, а работа может быть поставлена в очередь на отдельной нити, но без взаимного исключения.
    2. Петля исполнителя, в которой одна нить жертвует себя исполнителю для выполнения всех поставленных в очередь работ. Это связано с серийным исполнителем в том, что он гарантирует взаимное исключение, но вместо этого гарантирует, что конкретный поток будет выполнять работу. Они особенно полезны для целей тестирования, когда код берет на себя исполнитель, но тестирование кода требует контроля над выполнением.
    3. Исполнитель потоков GUI, где фреймворк GUI может подвергать интерфейс исполнителя, чтобы позволить другим потокам выстраивать работу в очередь, которая будет выполняться как часть потока GUI. Это ведет себя аналогично исполнителю цикла, но должно быть реализовано в виде пользовательского интерфейса как часть структуры.
  3. Исполнители inline, которые выполняют inline к потоку, который вызывает submit(). Это не имеет очередей и ведет себя как обычный исполнитель, но всегда использует нить вызывающего & #8217 для выполнения. Это позволяет параллельно выполнять работы. Этот тип исполнителя часто полезен, когда есть исполнитель, требуемый интерфейсом, но когда по причинам производительности лучше не стоять в очереди или переключать потоки. Это часто очень полезно в качестве оптимизации для продолжения работы, которая должна выполняться немедленно или быстро, а также может быть полезна для оптимизации, когда интерфейс требует исполнителя, но рабочие задачи слишком малы, чтобы оправдать накладные расходы на полный пул потоков.

Возникает вопрос, кого из этих исполнителей (или других) включить в эту библиотеку. Есть варианты использования для этих и многих других исполнителей. Часто полезно иметь более одного реализованного исполнителя (например, пул потоков), чтобы иметь более точный контроль над тем, где выполняется работа из-за существования потока графического интерфейса, или для целей тестирования. Некоторые основные исполнители часто полезны, и они были описаны здесь как ядро того, что должно быть в этой библиотеке, если общие случаи использования возникают для альтернативных реализаций исполнителя, они могут быть добавлены в будущем. Текущий набор представлен здесь: базовый пул потоков<basic_thread_pool>, серийный исполнитель<serial_executor>, петлевый исполнитель<loop_executor>, встроенный исполнитель<inline_executor>и нитевидный исполнитель<thread_executor>.

#include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/future.hpp>
#include <numeric>
#include <algorithm>
#include <functional>
#include <iostream>
#include <list>
template<typename T>
struct sorter
{
    boost::basic_thread_pool pool;
    typedef std::list<T> return_type;
    std::list<T> do_sort(std::list<T> chunk_data)
    {
        if(chunk_data.empty()) {
            return chunk_data;
        }
        std::list<T> result;
        result.splice(result.begin(),chunk_data, chunk_data.begin());
        T const& partition_val=*result.begin();
        typename std::list<T>::iterator divide_point =
            std::partition(chunk_data.begin(), chunk_data.end(),
                           [&](T const& val){return val<partition_val;});
        std::list<T> new_lower_chunk;
        new_lower_chunk.splice(new_lower_chunk.end(), chunk_data,
                               chunk_data.begin(), divide_point);
        boost::future<std::list<T> > new_lower =
             boost::async(pool, &sorter::do_sort, this, std::move(new_lower_chunk));
        std::list<T> new_higher(do_sort(chunk_data));
        result.splice(result.end(),new_higher);
        while(!new_lower.is_ready()) {
            pool.schedule_one_or_yield();
        }
        result.splice(result.begin(),new_lower.get());
        return result;
    }
};
template<typename T>
std::list<T> parallel_quick_sort(std::list<T>& input) {
    if(input.empty()) {
        return input;
    }
    sorter<T> s;
    return s.do_sort(input);
}

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

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

Исполнитель — это объект, который планирует закрытие, которое было представлено ему, обычно асинхронно. Может быть несколько моделей класса «Исполнитель». Некоторые конкретные дизайнерские примечания:

  • Бассейны с нитями являются хорошо известными моделями концепции Исполнителя, и эта библиотека действительно включает в себя базовый класс_thread_pool, но существуют и другие реализации, в том числе возможность планировать работу над потоками графического интерфейса, планировать работу над потоком доноров, а также несколько специализаций потоковых пулов.
  • Выбор, какой исполнитель использовать, ясен. Это важно по причинам, описанным в разделе «Мотивация». В частности, рассмотрим распространенный случай асинхронной операции, которая сама порождает асинхронные операции. Если бы обе операции выполнялись одним и тем же исполнителем, и если бы у этого исполнителя было ограниченное количество рабочих ниток, то мы могли бы зайти в тупик. Программы часто решают такие проблемы путем разделения различных видов работы между различными исполнителями.
  • Даже если в наличии исполнителя по умолчанию может быть большая ценность, которую можно использовать, когда детальный контроль не нужен, авторы не знают, как реализовать его портативным и надежным способом.
  • Библиотека предоставляет Исполнителей на основе статического и динамического полиморфизма. Интерфейс статического полиморфизма предназначен для использования в контекстах, которые должны иметь лучшие характеристики. Интерфейс динамического полиморфизма имеет то преимущество, что он может изменять исполнителя, который использует функцию, не делая его шаблоном, и может передавать исполнителей через бинарный интерфейс. Для некоторых приложений стоимость дополнительной виртуальной отправки может быть почти наверняка незначительной по сравнению с другими задействованными операциями.
  • Концептуально исполнитель ставит закрытия в очередь и в какой-то момент выполняет их. Очередь всегда неограничена, поэтому добавление закрытия исполнителю никогда не блокирует. (Определение “ никогда не блокирует ” формально является сложной задачей, но неофициально мы просто имеем в виду, что отправка () является обычной функцией, которая выполняет что-то и возвращает, а не ждет завершения какой-то потенциально длительной работы в другом потоке.)
Closure

Один из важных вопросов заключается в том, что такое закрытие. Эта библиотека имеет очень простой ответ: закрытие — это<Callable>без параметров и возвращение<void>.

N3785 выбирает более конкретный<std::function<void()>>, поскольку он обеспечивает только динамический полиморфизм и утверждает, что на практике реализация шаблонного подхода или другого подхода непрактична. Авторы этой библиотеки считают, что подход, основанный на шаблонах, совместим с динамическим подходом. Они приводят некоторые аргументы:

Во-первых, виртуальная функция не может быть шаблоном. Это верно, но также верно и то, что интерфейс исполнителя может предоставлять шаблонные функции, которые вызывают виртуальные общедоступные функции. Другая причина, которую они приводят, заключается в том, что "параметр шаблона усложнит интерфейс без добавления какой-либо реальной общности. В конце концов, классу исполнителей потребуется какое-то стирание типа для обработки всех различных типов функциональных объектов с подписью<void()>, и это & # 8217; именно то, что уже делает std:: Функция». Мы считаем, что исполнитель должен управлять этими деталями реализации, а не пользователь.

Мы разделяем все аргументы, которые они приводят, связанные с интерфейсом рабочего блока<void()>. Рабочая единица — это закрытие, которое не принимает никаких аргументов и не возвращает никакой ценности. Это действительно ограничение на пользовательский код, но в сочетании с<boost::async>принятием исполнителей в качестве параметров у пользователя есть все, что ему нужно.

Третий связан с производительностью. Они утверждают, что "любой механизм хранения затворов на очереди исполнителя’ должен будет использовать некоторую форму стирания типа. Нет никаких оснований полагать, что пользовательский механизм закрытия, написанный только для std::executor и нигде больше не используемый в стандартной библиотеке, был бы в этом отношении лучше, чем<std::function<void()>>. Мы считаем, что реализация может сделать лучше, чем хранение закрытия на<std::function<void()>>. Например, реализация может использовать интрузивные данные для хранения закрытия и указатели на другие узлы, необходимые для хранения закрытий в заданном порядке.

Кроме того,<std::function<void()>>не может быть построен путем перемещения затвора, поэтому, например,<std::packaged_task>не может быть Закрытием.

Scheduled work

Подход этой библиотеки к плановой работе по предложению N3785 совершенно иной. Вместо добавления запланированных операций к определенному графику Полиморфный интерфейс исполнителя, мы выбираем, добавляя конкретный класс<scheduler>, который не является исполнителем и знает, как управлять с планированием временных задач<submit_at>/<submit_after>.

<scheduler>обеспечивает заводы-исполнители<at>/<after>с учетом конкретного<time_point>или<duration>. Построенные исполнители обертывают ссылку на этот планировщик и время, в которое будет выполнено представленное задание.

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

sch.on(tp).after(seconds(i)).submit(boost::bind(fn,i));

Это имеет ряд преимуществ:

  • Запланированные операции доступны для всех исполнителей через обертки.
  • Функции шаблона могут принимать любые хроно<time_point>и<duration>соответственно, поскольку мы не работаем с виртуальными функциями.

Чтобы управлять всеми часами, эта библиотека предлагает общее решение.<scheduler<Clock>>знать, как управлять задачами<submit_at>/<submit_after><Clock::time_point>/<Clock::duration>. Обратите внимание, что продолжительность на разных часах отличается.

Not Handled Exceptions

Как и в N3785 и на основании того же дизайнерского решения, что и<std>/<boost::thread>, если закрытие пользователя бросает исключение, исполнитель должен вызвать функцию<std::terminate>. Обратите внимание, что когда мы объединим<boost::async>и<Executors>, исключение будет поймано закрытием, связанным с возвращенным будущим, так что исключение хранится на возвращенном будущем, как и на других<async>перегрузках.

At thread entry

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

Для исполнителей, которые не инстанцируют нить и которые будут использовать текущую нить, эта функция должна называться только для нити, вызывающей функцию<at_thread_entry>.

Cancelation

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

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

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

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

Current executor

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

Причина в том, что пользователь всегда может использовать переменную thread_local и сбросить ее с помощью функции<at_thread_entry >.

thread_local current_executor_state_type current_executor_state;
executor* current_executor() { return current_executor_state.current_executor(); }
basic_thread_pool pool(
	// at_thread_entry
	[](basic_thread_pool& pool) {
		current_executor_state.set_current_executor(pool);
	}
);

[править]

Default executor

Авторы библиотеки разделяют некоторые опасения комитета по стандарту C++ (введение нового единого общего ресурса, синглтона, может затруднить его переносимость во все среды), и что эта библиотека не должна предоставлять исполнителя по умолчанию в течение некоторого времени.

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

boost::generic_executor_ref default_executor()
{
    static boost::basic_thread_pool tp(4);
    return generic_executor_ref(tp);
}

Тип<E>соответствует требованиям<Closure>, если это модель<Callable(void())>и модель<CopyConstructible>/<MoveConstructible>.

Концепция<Executor>моделирует общие операции всех исполнителей.

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

  • e.submit(lc);
  • e.submit(rc);
  • e.close();
  • b= e.closed();
  • e.try_executing_one();
  • e.reschedule_until(p);

где

  • eобозначает значение типаE,
  • lcобозначает значение l типаClosure,
  • rcобозначает значение r типаClosure
  • pобозначает значение типаPredicate

Effects:

Указанное закрытие планируется к исполнению в какой-то момент в будущем. Если вызванное закрытие бросает исключение, исполнитель вызовет std::terminate, как в случае с нитями.

Synchronization:

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

Return type:

<void>.

Throws:

sync_queue_is_closed, если резьба закрыта. Любое исключение, которое можно бросить при хранении затвора.

Exception safety:

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

Effects:

Указанное закрытие планируется к исполнению в какой-то момент в будущем. Если вызванное закрытие бросает исключение, исполнитель вызовет std::terminate, как в случае с нитями.

Synchronization:

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

Return type:

<void>.

Throws:

sync_queue_is_closed, если резьба закрыта. Любое исключение, которое можно бросить при хранении затвора.

Exception safety:

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

Effects:

Закрыть исполнителя<e>для представлений.

Remark:

Рабочие нити будут работать до тех пор, пока не будет больше закрытий.

Return type:

<void>.

Throws:

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

Exception safety:

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

Return type:

<bool>.

Return:

является ли исполнитель закрытым для представления.

Throws:

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

Effects:

Попробуйте выполнить одну работу.

Remark:

Была ли выполнена работа.

Return type:

<bool>.

Return:

Была ли выполнена работа.

Throws:

Что бы ни бросал нынешний конструктор работ или<work()>броски.

Requires:

Это должно быть вызвано из запланированной работы.

Effects:

Перепланировка продолжается до 2737 года.

Return type:

<bool>.

Return:

Была ли выполнена работа.

Throws:

Что бы ни бросал нынешний конструктор работ или<work()>броски.

#include <boost/thread/work.hpp>
namespace boost {
  typedef 'implementation_defined' work;
}

Requires:

Работа является моделью «закрытия» "

Исполнитель абстрактного базового класса.

#include <boost/thread/executor.hpp>
namespace boost {
  class executor
  {
  public:
    typedef  boost::work work;
    executor(executor const&) = delete;
    executor& operator=(executor const&) = delete;
    executor();
    virtual ~executor() {};
    virtual void close() = 0;
    virtual bool closed() = 0;
    virtual void submit(work&& closure) = 0;
    virtual void submit(work& closure) = 0;
    template <typename Closure>
    void submit(Closure&& closure);
    virtual bool try_executing_one() = 0;
    template <typename Pred>
    bool reschedule_until(Pred const& pred);
  };
}
executor();

Effects:

Создает исполнителя.

Throws:

Ничего.

virtual ~executor();

Effects:

Уничтожает исполнителя.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Полиморфный адаптер модели Исполнителя к Исполнителю.

#include <boost/thread/executor.hpp>
namespace boost {
  template <typename Executor>
  class executor_adaptor : public executor
  {
    Executor ex; // for exposition only
  public:
    typedef  executor::work work;
    executor_adaptor(executor_adaptor const&) = delete;
    executor_adaptor& operator=(executor_adaptor const&) = delete;
    template <typename ...Args>
    executor_adaptor(Args&& ... args);
    Executor& underlying_executor() noexcept;
    void close();
    bool closed();
    void submit(work&& closure);
    void submit(work& closure);
    bool try_executing_one();
  };
}
template <typename ...Args>
executor_adaptor(Args&& ... args);

Effects:

Построение executor_adaptor.

Throws:

Ничего.

virtual ~executor_adaptor();

Effects:

Уничтожает исполнителя_адаптора.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Executor& underlying_executor() noexcept;

Return:

Подлежащая исполнительная инстанция.

Исполнитель абстрактного базового класса.

#include <boost/thread/generic_executor_ref.hpp>
namespace boost {
  class generic_executor_ref
  {
  public:
    generic_executor_ref(generic_executor_ref const&);
    generic_executor_ref& operator=(generic_executor_ref const&);
    template <class Executor>
    generic_executor_ref(Executor& ex);
    generic_executor_ref() {};
    void close() = 0;
    bool closed() = 0;
    template <typename Closure>
    void submit(Closure&& closure);
    virtual bool try_executing_one() = 0;
    template <typename Pred>
    bool reschedule_until(Pred const& pred);
  };
}

Планировщик, обеспечивающий функции, связанные со временем. Обратите внимание, что<scheduler>не является исполнителем.

#include <boost/thread/executors/scheduler.hpp>
namespace boost {
  template <class Clock=steady_clock>
  class scheduler
  {
  public:
    using work = boost::function<void()> ;
    using clock = Clock;
    scheduler(scheduler const&) = delete;
    scheduler& operator=(scheduler const&) = delete;
    scheduler();
    ~scheduler();
    void close();
    bool closed();
    template <class Duration, typename Closure>
    void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
    template <class Rep, class Period, typename Closure>
    void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
    template <class Duration>
    at_executor<scheduler> submit_at(chrono::time_point<clock,Duration> abs_time);
    template <class Rep, class Period>
    at_executor<scheduler> submit_after(chrono::duration<Rep,Period> rel_time);
    template <class Executor>
    scheduler_executor_wrapper<scheduler, Executor> on(Executor& ex);
  };
}
scheduler();

Effects:

Построение А<scheduler>.

Throws:

Ничего.

~scheduler();

Effects:

Уничтожает планировщик.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

template <class Clock, class Duration, typename Closure>
void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);

Effects:

А<closure>будет исполнено в<abs_time>.

Throws:

Ничего.

template <class Rep, class Period, typename Closure>
void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);

Effects:

А<closure>будет исполнено после<rel_time>.

Throws:

Ничего.

#include <boost/thread/executors/scheduler.hpp>
namespace boost {
  template <class Scheduler>
  class at_executor
  {
  public:
    using work = Scheduler::work;
    using clock = Scheduler::clock;
    at_executor(at_executor const&) = default;
    at_executor(at_executor &&) = default;
    at_executor& operator=(at_executor const&) = default;
    at_executor& operator=(at_executor &&) = default;
    at_executor(Scheduler& sch, clock::time_point const& tp);
    ~at_executor();
    void close();
    bool closed();
    Scheduler& underlying_scheduler();
    template <class Closure>
    void submit(Closure&& closure);
    template <class Duration, typename Work>
    void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
    template <class Rep, class Period, typename Work>
    void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
    template <class Executor>
    resubmit_at_executor<Scheduler, Executor> on(Executor& ex);
  };
}
at_executor(Scheduler& sch, clock::time_point const& tp);

Effects:

Построено<at_executor>.

Throws:

Ничего.

~at_executor();

Effects:

Уничтожает<at_executor>.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Scheduler& underlying_scheduler() noexcept;

Return:

Основной пример планировщика.

template <typename Closure>
void submit(Closure&& closure);

Effects:

Назначьте<closure>для исполнения в<abs_time>дан в момент строительства.

Throws:

Ничего.

template <class Clock, class Duration, typename Closure>
void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);

Effects:

А<closure>будет исполнено в<abs_time>.

Throws:

Ничего.

template <class Rep, class Period, typename Closure>
void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);

Effects:

А<closure>будет исполнено после<rel_time>.

Throws:

Ничего.

#include <boost/thread/executors/scheduler.hpp>
namespace boost {
  template <class Scheduler, class Executor>
  class scheduler_executor_wrapper
  {
  public:
    using work = Scheduler::work;
    using clock = Scheduler::clock;
    scheduler_executor_wrapper(scheduler_executor_wrapper const&) = default;
    scheduler_executor_wrapper(scheduler_executor_wrapper &&) = default;
    scheduler_executor_wrapper& operator=(scheduler_executor_wrapper const&) = default;
    scheduler_executor_wrapper& operator=(scheduler_executor_wrapper &&) = default;
    scheduler_executor_wrapper(Scheduler& sch, Executor& ex);
    ~scheduler_executor_wrapper();
    void close();
    bool closed();
    Executor& underlying_executor();
    Scheduler& underlying_scheduler();
    template <class Closure>
    void submit(Closure&& closure);
    template <class Duration, typename Work>
    void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
    template <class Rep, class Period, typename Work>
    void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
    template <class Duration>
    resubmit_at_executor<Scheduler, Executor> at(chrono::time_point<clock,Duration> abs_time);
    template <class Rep, class Period>
    resubmit_at_executor<Scheduler, Executor> after(chrono::duration<Rep,Period> rel_time);
  };
}
scheduler_executor_wrapper(Scheduler& sch, Executor& ex);

Effects:

Построение А<scheduler_executor_wrapper>.

Throws:

Ничего.

~scheduler_executor_wrapper();

Effects:

Уничтожает<scheduler_executor_wrapper>.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Scheduler& underlying_scheduler() noexcept;

Return:

Основной пример планировщика.

Executor& underlying_executor() noexcept;

Return:

Подлежащая исполнительная инстанция.

template <typename Closure>
void submit(Closure&& closure);

Effects:

Поставьте<closure>на нижележащего исполнителя.

Throws:

Ничего.

template <class Clock, class Duration, typename Closure>
void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);

Effects:

Повторить<closure>, чтобы быть выполненным на нижележащем исполнителе в<abs_time>.

Throws:

Ничего.

template <class Rep, class Period, typename Closure>
void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);

Effects:

Повторить<closure>, чтобы быть выполненным на нижележащем исполнителе после<rel_time>.

Throws:

Ничего.

<Executor>обертывание<Scheduler>,<Executor>и<time_point>, обеспечивающее<Executor>интерфейс.

#include <boost/thread/executors/scheduler.hpp>
namespace boost {
  template <class Scheduler, class Executor>
  class resubmit_at_executor
  {
  public:
    using work = Scheduler::work;
    using clock = Scheduler::clock;
    resubmit_at_executor(resubmit_at_executor const&) = default;
    resubmit_at_executor(resubmit_at_executor &&) = default;
    resubmit_at_executor& operator=(resubmit_at_executor const&) = default;
    resubmit_at_executor& operator=(resubmit_at_executor &&) = default;
    template <class Duration>
    resubmit_at_executor(Scheduler& sch, Executor& ex, clock::time_point<Duration> const& tp);
    ~resubmit_at_executor();
    void close();
    bool closed();
    Executor& underlying_executor();
    Scheduler& underlying_scheduler();
    template <class Closure>
    void submit(Closure&& closure);
    template <class Duration, typename Work>
    void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
    template <class Rep, class Period, typename Work>
    void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
  };
}
template <class Duration>
resubmit_at_executor(Scheduler& sch, Executor& ex, clock::time_point<Duration> const& tp);

Effects:

Построено<resubmit_at_executor>.

Throws:

Ничего.

~resubmit_at_executor();

Effects:

Уничтожает исполнителя_адаптора.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Executor& underlying_executor() noexcept;

Return:

Подлежащая исполнительная инстанция.

Scheduler& underlying_scheduler() noexcept;

Return:

Основной пример планировщика.

template <typename Closure>
void submit(Closure&& closure);

Effects:

Повторно представить<closure>к исполнению на нижележащем исполнителе в<abs_time>данный в момент строительства.

Throws:

Ничего.

template <class Clock, class Duration, typename Closure>
void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);

Effects:

Повторить<closure>, чтобы быть выполненным на нижележащем исполнителе в<abs_time>.

Throws:

Ничего.

template <class Rep, class Period, typename Closure>
void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);

Effects:

Повторить<closure>, чтобы быть выполненным на нижележащем исполнителе после<rel_time>.

Throws:

Ничего.

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

#include <boost/thread/serial_executor.hpp>
namespace boost {
  template <class Executor>
  class serial_executor
  {
  public:
    serial_executor(serial_executor const&) = delete;
    serial_executor& operator=(serial_executor const&) = delete;
    template <class Executor>
    serial_executor(Executor& ex);
    Executor& underlying_executor() noexcept;
    void close();
    bool closed();
    template <typename Closure>
    void submit(Closure&& closure);
    bool try_executing_one();
    template <typename Pred>
    bool reschedule_until(Pred const& pred);
  };
}
template <class Executor>
serial_executor(Executor& ex);

Effects:

Построение последовательного_исполнителя.

Throws:

Ничего.

~serial_executor();

Effects:

Уничтожает последовательного исполнителя.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

generic_executor_ref& underlying_executor() noexcept;

Return:

Подлежащая исполнительная инстанция.

Throws:

Ничего.

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

#include <boost/thread/generic_serial_executor.hpp>
namespace boost {
  class generic_serial_executor
  {
  public:
    generic_serial_executor(generic_serial_executor const&) = delete;
    generic_serial_executor& operator=(generic_serial_executor const&) = delete;
    template <class Executor>
    generic_serial_executor(Executor& ex);
    generic_executor_ref& underlying_executor() noexcept;
    void close();
    bool closed();
    template <typename Closure>
    void submit(Closure&& closure);
    bool try_executing_one();
    template <typename Pred>
    bool reschedule_until(Pred const& pred);
  };
}
template <class Executor>
generic_serial_executor(Executor& ex);

Effects:

Построение последовательного_исполнителя.

Throws:

Ничего.

~generic_serial_executor();

Effects:

Уничтожает последовательного исполнителя.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Executor& underlying_executor() noexcept;

Return:

Подлежащая исполнительная инстанция.

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

#include <boost/thread/inline_executor.hpp>
namespace boost {
  class inline_executor
  {
  public:
    inline_executor(inline_executor const&) = delete;
    inline_executor& operator=(inline_executor const&) = delete;
    inline_executor();
    void close();
    bool closed();
    template <typename Closure>
    void submit(Closure&& closure);
    bool try_executing_one();
    template <typename Pred>
    bool reschedule_until(Pred const& pred);
  };
}
inline_executor();

Effects:

Построение inline_executor.

Throws:

Ничего.

~inline_executor();

Effects:

Уничтожает inline_executor.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Пул потоков с фиксированным количеством нитей.

#include <boost/thread/executors/basic_thread_pool.hpp>
namespace boost {
  class basic_thread_pool
  {
  public:
    basic_thread_pool(basic_thread_pool const&) = delete;
    basic_thread_pool& operator=(basic_thread_pool const&) = delete;
    basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency());
    template <class AtThreadEntry>
    basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
    ~basic_thread_pool();
    void close();
    bool closed();
    template <typename Closure>
    void submit(Closure&& closure);
    bool try_executing_one();
    template <typename Pred>
    bool reschedule_until(Pred const& pred);
  };
}

Effects:

Создает нитевой пул, который закрывает<thread_count>нитей.

Throws:

Какое бы исключение ни было сделано при инициализации необходимых ресурсов.

~basic_thread_pool();

Effects:

Уничтожает бассейн с нитями.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Нить_исполнитель с нитками для каждой задачи.

#include <boost/thread/executors/thread_executor.hpp>
namespace boost {
  class thread_executor
  {
  public:
    thread_executor(thread_executor const&) = delete;
    thread_executor& operator=(thread_executor const&) = delete;
    thread_executor();
    template <class AtThreadEntry>
    basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
    ~thread_executor();
    void close();
    bool closed();
    template <typename Closure>
    void submit(Closure&& closure);
  };
}

Effects:

Создает файл thread_executor.

Throws:

Какое бы исключение ни было сделано при инициализации необходимых ресурсов.

~thread_executor();

Effects:

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

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

Пользователь назначил исполнителя.

#include <boost/thread/loop_executor.hpp>
namespace boost {
  class loop_executor
  {
  public:
    loop_executor(loop_executor const&) = delete;
    loop_executor& operator=(loop_executor const&) = delete;
    loop_executor();
    ~loop_executor();
    void close();
    bool closed();
    template <typename Closure>
    void submit(Closure&& closure);
    bool try_executing_one();
    template <typename Pred>
    bool reschedule_until(Pred const& pred);
    void loop();
    void run_queued_closures();
  };
}
loop_executor();

Effects:

Создает исполнителя, который запускает закрытие, используя один из методов закрытия.

Throws:

Какое бы исключение ни было сделано при инициализации необходимых ресурсов.

virtual ~loop_executor();

Effects:

Уничтожает исполнителя.

Synchronization:

Завершение всех замыканий происходит до завершения разрушителя-исполнителя.

void loop();

Return:

Перенесённый график работает до 2774 года или пуст.

Throws:

Что бы ни бросал нынешний конструктор работ или<work()>броски.

void run_queued_closures();

Return:

Перенесите очерченные работы.

Throws:

Что бы ни бросал нынешний конструктор работ или<work()>броски.

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

Это делается посредством предоставления четырех шаблонов классов:<future>и<boost::shared_future>, которые используются для получения асинхронных результатов, и<boost::promise>и<boost::packaged_task>, которые используются для генерации асинхронных результатов.

Пример<future>содержит единственную ссылку на результат. Владение может быть передано между экземплярами с использованием конструктора хода или оператора назначения хода, но в большинстве случаев один экземпляр содержит ссылку на заданный асинхронный результат. Когда результат готов, он возвращается из<boost::future<R>::get()>путем ссылки на значение, чтобы позволить результату перемещаться или копироваться в соответствии с типом.

С другой стороны, многие экземпляры<boost::shared_future>могут ссылаться на тот же результат. Случаи могут быть свободно скопированы и назначены, и<boost::shared_future<R>::get()>возвращает<const>ссылку, так что несколько вызовов<boost::shared_future<R>::get()>безопасны. Вы можете переместить экземпляр<future>в экземпляр<boost::shared_future>, таким образом передавая право собственности на асинхронный результат, но не наоборот.

<boost::async>— простой способ выполнения асинхронных задач. Призыв к<boost::async>возвращает<future>, который будет содержать результат задания.

Вы можете ждать фьючерсы индивидуально или с одной из функций<boost::wait_for_any()>и<boost::wait_for_all()>.

Вы можете установить значение в будущем либо с<boost::promise>, либо с<boost::packaged_task>.<boost::packaged_task>является вызывающим объектом, который обертывает функцию или вызывающий объект. Когда упакованная задача вызывается, она, в свою очередь, вызывает содержащуюся функцию и заполняет будущее обратным значением. Это ответ на многолетний вопрос: «Как мне вернуть значение из нити?»: Упакуйте функцию, которую вы хотите запустить как<boost::packaged_task>, и передайте упакованную задачу конструктору резьбы. Будущее, извлеченное из упакованной задачи, затем может быть использовано для получения возвращаемого значения. Если функция выбрасывает исключение, то оно хранится в будущем вместо возвращаемого значения.

int calculate_the_answer_to_life_the_universe_and_everything(){
    return 42;}
boost::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything);boost:: future<int> fi=pt.get_future();
boost::thread task(boost::move(pt)); // launch task on a thread
fi.wait(); // wait for it to finish
assert(fi.is_ready());assert(fi.has_value());assert(!fi.has_exception());assert(fi.get_state()==boost::future_state::ready);assert(fi.get()==42);

<boost::promise>— это немного более низкий уровень: он просто предоставляет явные функции для хранения значения или исключения в связанном будущем. Таким образом, обещание может быть использовано там, где значение может быть получено из более чем одного возможного источника или где одна операция может дать несколько значений.

boost::promise<int> pi;boost:: future<int> fi;fi=pi.get_future();
pi.set_value(42);
assert(fi.is_ready());assert(fi.has_value());assert(!fi.has_exception());assert(fi.get_state()==boost::future_state::ready);assert(fi.get()==42);

Как<boost::promise>, так и<boost::packaged_task>поддерживаютобратные вызовы, которые вызываются, когда нить блокируется в вызове<wait()>или<timed_wait()>в будущем, которое ожидает результата от<boost::promise>или<boost::packaged_task>, в нити, которая выполняет ожидание. Они могут быть установлены с использованием функции члена<set_wait_callback()>на<boost::promise>или<boost::packaged_task>.

Это позволяетленивым фьючерсам, где результат фактически не вычисляется, пока он не понадобится какой-то нитью. В приведенном ниже примере вызов<f.get()>вызывает обратный вызов<invoke_lazy_task>, который запускает задачу для установки значения. Если вы удалите вызов<f.get()>, задача никогда не выполняется.

int calculate_the_answer_to_life_the_universe_and_everything(){
    return 42;}
void invoke_lazy_task(boost::packaged_task<int>& task){
    try
    {
        task();
    }
    catch(boost::task_already_started&)
    {}}
int main(){
    boost::packaged_task<int> task(calculate_the_answer_to_life_the_universe_and_everything);
    task.set_wait_callback(invoke_lazy_task);
    boost:: future<int> f(task.get_future());
    assert(f.get()==42);}

Отдельные потоки создают проблему для объектов с длительностью хранения потоков. Если мы используем механизм, отличный от<thread::__join>, чтобы ждать, пока<thread>завершит свою работу, например, ожидая, что будущее будет готово, то деструкторы резьбы конкретных переменных будут по-прежнему работать после возобновления потока ожидания. В этом разделе объясняется, как стандартный механизм может быть использован для обеспечения безопасности такой синхронизации, гарантируя, что объекты с длительностью хранения потока будут уничтожены до того, как будет подготовлено будущее. Например.

int find_the_answer(); // uses thread specific objects
void thread_func(boost::promise<int>&& p)
{
    p.set_value_at_thread_exit(find_the_answer());
}
int main()
{
    boost::promise<int> p;
    boost::thread t(thread_func,boost::move(p));
    t.detach(); // we're going to wait on the future
    std::cout<<p.get_future().get()<<std::endl;
}

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

Такие механизмы предусмотрены для<boost::condition_variable>,<boost::promise>и<boost::packaged_task>. Например.

void task_executor(boost::packaged_task<void(int)> task,int param)
{
    task.make_ready_at_thread_exit(param); // execute stored task
} // destroy thread specific and wake threads waiting on futures from task

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

boost::condition_variable cv;
boost::mutex m;
complex_type the_data;
bool data_ready;
void thread_func()
{
    boost::unique_lock<std::mutex> lk(m);
    the_data=find_the_answer();
    data_ready=true;
    boost::notify_all_at_thread_exit(cv,boost::move(lk));
} // destroy thread specific objects, notify cv, unlock mutex
void waiting_thread()
{
    boost::unique_lock<std::mutex> lk(m);
    while(!data_ready)
    {
        cv.wait(lk);
    }
    process(the_data);
}

Нить ожидания гарантирует, что конкретные объекты, используемые<thread_func()>, были уничтожены к моменту вызова<process(the_data)>. Если замок на<m>освобождается и вновь приобретается после установки<data_ready>и до вызова<boost::notify_all_at_thread_exit()>, то это НЕ удерживает, так как нить может вернуться из ожидания из-за ложного пробуждения.

<boost::async>— простой способ выполнения асинхронных задач с использованием доступной аппаратной параллели. Призыв к<boost::async>возвращает<boost::future>, который будет содержать результат задания. В зависимости от политики запуска, задача либо выполняется асинхронно на собственном потоке, либо синхронно на любом потоке, который вызывает<wait()>или<get()>функции члена на этом<future>.

Политика запуска либо boost::launch::async, которая просит время выполнения создать асинхронный поток, либо boost::launch::deferred, что указывает на то, что вы просто хотите отложить вызов функции до более позднего времени (ленивая оценка). Этот аргумент является необязательным - если вы опустите его, ваша функция будет использовать политику по умолчанию.

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

int parallel_sum(int* data, int size)
{
  int sum = 0;
  if ( size < 1000 )
    for ( int i = 0; i < size; ++i )
      sum += data[i];
  else {
    auto handle = boost::async(parallel_sum, data+size/2, size-size/2);
    sum += parallel_sum(data, size/2);
    sum += handle.get();
  }
  return sum;
}

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

Multiple get

Второй<get()>вызов в следующем примере не определен.

void bad_second_use( type arg ) {
  auto ftr = async( [=]{ return work( arg ); } );
    if ( cond1 )
    {
        use1( ftr.get() );
    } else
    {
        use2( ftr.get() );
    }
    use3( ftr.get() ); // second use is undefined
}

Использование<shared_future>решает проблему

void good_second_use( type arg ) {
   shared_future<type> ftr = async( [=]{ return work( arg ); } );
    if ( cond1 )
    {
        use1( ftr.get() );
    } else
    {
        use2(  ftr.get() );
    }
    use3( ftr.get() ); // second use is defined
}
share()

Необходимо указать тип возврата при объявлении<shared_future>; авто не доступно в списках аргументов шаблона. Здесь<share()>можно было бы использовать для упрощения кода

void better_second_use( type arg ) {
   auto ftr = async( [=]{ return work( arg ); } ).share();
    if ( cond1 )
    {
        use1( ftr.get() );
    } else
    {
        use2(  ftr.get() );
    }
    use3( ftr.get() ); // second use is defined
}
Writing on get()

Пользователь может читать или записывать будущие переменные.

void write_to_get( type arg ) {
   auto ftr = async( [=]{ return work( arg ); } ).share();
    if ( cond1 )
    {
        use1( ftr.get() );
    } else
    {
      if ( cond2 )
        use2(  ftr.get() );
      else
        ftr.get() = something(); // assign to non-const reference.  
    }
    use3( ftr.get() ); // second use is defined
}

Это работает, потому что функция<shared_future<>::get()>возвращает неконстантную ссылку на соответствующее хранилище. Конечно, доступ к этому хранилищу должен быть обеспечен пользователем. Библиотека не гарантирует, что доступ к внутреннему хранилищу безопасен.

Комитет по стандартизации C++ провел некоторую работу над<atomic_future>, который ведет себя как<atomic>переменная, то есть thread_safe, и<shared_future>, который может быть разделен между несколькими потоками, но не было достаточного консенсуса и времени, чтобы подготовить его к C++11.

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

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

make_ready_future

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

boost::future<int> compute(int x)
{
  if (x == 0) return boost::make_ready_future(0);
  if (x < 0) return boost::make_ready_future<int>(std::logic_error("Error"));
  boost::future<int> f1 = boost::async([]() { return x+1; });
  return f1;
}

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

В асинхронном программировании очень часто одна асинхронная операция по завершении вызывает вторую операцию и передает ей данные. Действующий стандарт C++ не позволяет зарегистрировать продолжение в будущем. С<.then>вместо ожидания результата «привязывается» к асинхронной операции продолжение, которое вызывается, когда результат готов. Продолжения, зарегистрированные с помощью функции<.then>, помогут избежать блокировки ожидания или потери потоков при опросе, значительно улучшая отзывчивость и масштабируемость приложения.

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

В примере ниже<future<string>><f2>зарегистрировано продолжение<future<int>><f1>с использованием функции<.then()>члена. Эта операция выполняет функцию лямбда, которая описывает, как<f2>следует действовать после того, как<f1>готов.

#include <boost/thread/future.hpp>
using namespace boost;
int main()
{
  future<int> f1 = async([]() { return 123; });
  future<string> f2 = f1.then([](future<int> f) { return f.get().to_string(); // here .get() won't block });
}

Одной из ключевых особенностей этой функции является способность цеплять несколько асинхронных операций. В асинхронном программировании принято определять последовательность операций, в которой каждое продолжение выполняется только после завершения предыдущего. В некоторых случаях предшествующее будущее создает ценность, которую продолжение принимает в качестве входной. Используя<future.then()>, создание цепочки продолжений становится простым и интуитивным:

myFuture.then(...).then(...).then(...).

Некоторые моменты следует отметить:

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

Параметры ввода:

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

Возвратные значения: Решение о возвращении будущего было основано, прежде всего, на способности цеплять множественные продолжения с помощью<.then()>. Это преимущество композитности дает программисту невероятный контроль и гибкость над своим кодом. Возвращение объекта<future>вместо<shared_future>также является гораздо более дешевой операцией, тем самым улучшая производительность. Объект<shared_future>не является необходимым для использования функции сцепления. Также легко перейти от<future>к<shared_future>при необходимости, используя будущее::share().

//#include <boost/thread/future.hpp>
namespace boost
{
  namespace future_state  // EXTENSION
  {
    enum state {uninitialized, waiting, ready, moved};
  }
  enum class future_errc
  {
    broken_promise,
    future_already_retrieved,
    promise_already_satisfied,
    no_state
  };
  enum class launch
  {
    none = unspecified,
    async = unspecified,
    deferred = unspecified,
    executor = unspecified,
    inherit = unspecified,
    any = async | deferred
  };
  enum class future_status {
    ready,  timeout, deferred
  };
  namespace system
  {
    template <>
    struct is_error_code_enum<future_errc> : public true_type {};
    error_code make_error_code(future_errc e);
    error_condition make_error_condition(future_errc e);
  }
  const system::error_category& future_category();
  class future_error;
  class exceptional_ptr;
  template <typename R>
  class promise;
  template <typename R>
  void swap(promise<R>& x, promise<R>& y) noexcept;
  namespace container {
    template <class R, class Alloc>
    struct uses_allocator<promise<R>, Alloc>:: true_type;
  }
  template <typename R>
  class future;
  template <typename R>
  class shared_future;
  template <typename S>
  class packaged_task;
  template <class S> void swap(packaged_task<S>&, packaged_task<S>&) noexcept;
  template <class S, class Alloc>
  struct uses_allocator<packaged_task <S>, Alloc>;
  template <class F>
    future<typename result_of<typename decay<F>::type()>::type>
    async(F f);
  template <class F>
    future<typename result_of<typename decay<F>::type()>::type>
    async(launch policy, F f);
  template <class F, class... Args>
    future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
    async(F&& f, Args&&... args);
  template <class F, class... Args>
    future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
    async(launch policy, F&& f, Args&&... args);
  template <class Executor, class F, class... Args>
    future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
    async(Executor &ex, F&& f, Args&&... args);
  template<typename Iterator>
    void wait_for_all(Iterator begin,Iterator end); // EXTENSION
  template<typename F1,typename... FS>
    void wait_for_all(F1& f1,Fs&... fs); // EXTENSION
  template<typename Iterator>
    Iterator wait_for_any(Iterator begin,Iterator end); // EXTENSION
  template<typename F1,typename... Fs>
    unsigned wait_for_any(F1& f1,Fs&... fs); // EXTENSION
  template <class InputIterator>
    future<std::vector<typename InputIterator::value_type::value_type>>
    when_all(InputIterator first, InputIterator last);
  template <typename... T>
    future<std::tuple<decay_t<T>...> when_all(T&&... futures);
  template <class InputIterator>
    future<std::vector<typename InputIterator::value_type::value_type>>
    when_any(InputIterator first, InputIterator last); // EXTENSION
  template <typename... T>
    future<std::tuple<decay_t<T>...> when_any(T&&... futures);
  template <typename T>
    future<typename decay<T>::type> make_future(T&& value);  // DEPRECATED
  future<void> make_future();  // DEPRECATED
  template <typename T>
    future<typename decay<T>::type> make_ready_future(T&& value);  // EXTENSION
  future<void> make_ready_future();  // EXTENSION
  exceptional_ptr make_exceptional_future(exception_ptr ex);  // EXTENSION
  template <typename E>
    exceptional_ptr make_exceptional_future(E ex);  // EXTENSION
  exceptional_ptr make_exceptional_future();  // EXTENSION
  template <typename T>
  shared_future<typename decay<T>::type> make_shared_future(T&& value);  // DEPRECATED
  shared_future<void> make_shared_future();  // DEPRECATED
namespace future_state
{
  enum state {uninitialized, waiting, ready, moved};
}
 enum class future_errc
 {
   broken_promise = implementation defined,
   future_already_retrieved = implementation defined,
   promise_already_satisfied = implementation defined,
   no_state = implementation defined
 }
The enum values of future_errc are distinct and not zero.
enum class launch
{
  none = unspecified,
  async = unspecified,
  deferred = unspecified,
  executor = unspecified,
  inherit = unspecified,
  any = async | deferred
};

Запуск типа enum — это тип битмаски с запуском::async и Launch::deferred, обозначающий отдельные биты.

Будущее, созданное с<promise<>>или с<packaged_task<>>или с<make_ready_future>/<make_exceptional_future>(не имеет связанной политики запуска), имеет подразумеваемую политику запуска<launch::none>.

Будущее, созданное<async(launch::async,...)>или<::then(launch::async,...)>, связано с политикой запуска<launch::async>. Будущее, созданное<async(launch::deferred,...)>или<::then(launch::deferred,...)>, связано с политикой запуска<launch::deferred>. Будущее, созданное<async(Executor,...)>или<::then(Executor,...)>или<::then(launch::executor,...)>, связано с политикой запуска<launch::executor>. Будущее, созданное<async(...)>или<::then(...)>, связано с политикой запуска<launch::none>.

Будущее, созданное<::then(launch::inherit,...)>, связано с политикой запуска родительского будущего.

Политики запуска<executor>и<inherit>имеют смысл только для пользователей<then()>.

namespace system
{
  template <>
  struct is_error_code_enum<future_errc> : public true_type {};
}
namespace system
{
  error_code make_error_code(future_errc e);
}

Returns:

<error_code(static_cast<int>(e), future_category())>.

namespace system
{
  error_condition make_error_condition(future_errc e);
}

Returns:

<error_condition(static_cast<int>(e),future_category())>.

const system::error_category& future_category();

Returns:

Ссылка на объект типа, полученная из категории ошибок класса.

Notes:

Объект<default_error_condition>и эквивалентные виртуальные функции ведут себя так, как указано для класса<system::error_category>. Виртуальная функция объекта<name>возвращает указатель на строку «будущее».

class future_error
    : public std::logic_error
{
public:
    future_error(system::error_code ec);
    const system::error_code& code() const no_except;
};
future_error(system::error_code ec);

Effects:

Построение будущей ошибки.

Postconditions:

<code()==ec>

Throws:

Ничего.

const system::error_code& code() const no_except;

Returns:

Значение<ec>, переданное конструктору объекта.

enum class future_status {
  ready,  timeout, deferred
};
class exceptional_ptr
{
public:
  exceptional_ptr();
  explicit exceptional_ptr(exception_ptr ex);
  template <class E>
  explicit exceptional_ptr(E&& ex);
};
exceptional_ptr();
explicit exceptional_ptr(exception_ptr ex);
template <class E>
explicit exceptional_ptr(E&& ex);

Effects:

Исключение, которое передается конструктору, или текущее исключение, если в построенный<exceptional_ptr>не перемещается параметр, если это значение r. В противном случае исключение копируется в построенное<exceptional_ptr>.

Postconditions:

<valid() ==true &&is_ready()= true&& has_value() =false>

Throws:

Ничего.

template <typename R>class  future{
public:
  typedef R value_type;  // EXTENSION
   future( future const& rhs) = delete;
   future& operator=( future const& rhs) = delete;
   future() noexcept;
  ~ future();
  // move support
   future( future && other) noexcept;
  explicit  future( future< future<R>>&& rhs);  // EXTENSION
   future& operator=( future && other) noexcept;
  // factories
  shared_future<R> share();
  template<typename F>
   future<typename boost::result_of<F( future)>::type>
  then(F&& func); // EXTENSION
  template<typename S, typename F>
   future<typename boost::result_of<F( future)>::type>
  then(Ex& executor, F&& func); // EXTENSION
  template<typename F>
   future<typename boost::result_of<F( future)>::type>
  then(launch policy, F&& func); // EXTENSION
  see below unwrap();  // EXTENSION
   future fallback_to();  // EXTENSION
  void swap( future& other) noexcept;
  // retrieving the value
  see below get();
  see below get_or(see below);  // EXTENSION
  exception_ptr get_exception_ptr(); // EXTENSION
  // functions to check state
  bool valid() const noexcept;
  bool is_ready() const; // EXTENSION
  bool has_exception() const; // EXTENSION
  bool has_value() const; // EXTENSION
  // waiting for the result to be ready
  void wait() const;
  template <class Rep, class Period>
  future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
  template <class Clock, class Duration>
  future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
#if defined BOOST_THREAD_USES_DATE_TIME
  template<typename Duration>
  bool timed_wait(Duration const& rel_time) const; // DEPRECATED SINCE V3.0.0
  bool timed_wait_until(boost::system_time const& abs_time) const; // DEPRECATED SINCE V3.0.0#endif
  typedef future_state::state state;  // EXTENSION
  state get_state() const;  // EXTENSION};
 future();

Effects:

Построение неинициализированного<future>.

Postconditions:

<this->is_ready>возвращается<false>.<this->get_state()>возвращается<boost::future_state::uninitialized>.

Throws:

Ничего.

~ future();

Effects:

Уничтожает<*this>.

Throws:

Ничего.

 future( future && other);

Effects:

Постраивает новое<future>и передает право собственности на совместное государство, связанное с<other><*this>.

Postconditions:

<this->get_state()>возвращает значение<other->get_state()>до вызова.<other->get_state()>возвращается<boost::future_state::uninitialized>. Если<other>был связан с общим состоянием, то этот результат теперь связан с<*this>.<other>не связан с каким-либо общим состоянием.

Throws:

Ничего.

Notes:

Если компилятор не поддерживает rvalue-ссылки, это реализуется с помощью boost. Эмуляция движения резьбы.

explicit  future( future< future<R>>&& other);  // EXTENSION
[Warning]Warning

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

Requires:

<other.valid()>.

[Последствия:

Он строит новое<future>и передает право собственности на общее состояние, связанное с<other>и раскручивает внутреннее будущее (см.<unwrap()>).

Postconditions:

<this->get_state()>возвращает значение<other->get_state()>до вызова.<other->get_state()>возвращается<boost::future_state::uninitialized>. Ассоциированное общее состояние теперь незавернуто, и внутреннее будущее общее состояние связано с<*this>.<other>не связано с каким-либо общим состоянием<! other.valid()>.

Throws:

Ничего.

Notes:

Если компилятор не поддерживает rvalue-ссылки, это реализуется с помощью boost. Эмуляция движения резьбы.

 future& operator=( future && other);

Effects:

Передача права собственности на долевое государство, связанное с<other><*this>.

Postconditions:

<this->get_state()>возвращает значение<other->get_state()>до вызова.<other->get_state()>возвращается<boost::future_state::uninitialized>. Если<other>был связан с общим состоянием, то этот результат теперь связан с<*this>.<other>не связан с каким-либо общим состоянием. Если<*this>ассоциировался с асинхронным результатом до вызова, то этот результат больше не имеет ассоциированного<future>экземпляра.

Throws:

Ничего.

Notes:

Если компилятор не поддерживает rvalue-ссылки, это реализуется с помощью boost. Эмуляция движения резьбы.

void swap( future & other) no_except;

Effects:

Смена собственности на общие государства, связанные с<other>и<*this>.

Postconditions:

<this->get_state()>возвращает значение<other->get_state()>до вызова.<other->get_state()>возвращает значение<this->get_state()>до вызова. Если<other>был связан с общим состоянием, то этот результат теперь связан с<*this>, в противном случае<*this>не имеет связанного результата. Если<*this>было связано с общим состоянием, то этот результат теперь связан с<other>, в противном случае<other>не имеет связанного результата.

Throws:

Ничего.

R get();R&  future<R&>::get();void  future<void>::get();

Effects:

Если<*this>связано с общим состоянием, подождите, пока результат будет готов, если по вызову<boost::future<R>::wait()>, и получите результат (будь то значение или исключение).

Returns:

-<future><R&>::получить[]вернуть сохраненную ссылку.

-<future><void>::get[], возвратного значения нет.

-<future><R>::получить[]возвращает r-ссылку на значение, хранящееся в общем состоянии.

Postconditions:

<this->is_ready()>возвращается<true>.<this->get_state()>возвращается<boost::future_state::ready>.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, хранящееся в общем состоянии вместо ценности.

Notes:

<get()>являетсяточкой прерывания.

R get_or(R&& v); // EXTENSIONR get_or(R const& v);  // EXTENSIONR&  future<R&>::get_or(R& v);  // EXTENSIONvoid  future<void>::get_or();  // EXTENSION
[Warning]Warning

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

Effects:

Если<*this>связано с общим состоянием, ожидайте, пока результат не будет готов, если по вызову<boost::future<R>::wait()>, и в зависимости от того, получит ли общее состояние<has_value()>результат.

Returns:

-<future><R&>::get_orvвозвращают сохраненную ссылку, если имеет_значение() и пропускает параметр иначе.

-<future><void>::get_or[], возвратного значения нет, но функция не бросает, даже если общее состояние содержало исключение.

-<future><R>::get_orvвозвращает r-ссылку на значение, хранящееся в общем состоянии, если<has_value()>и r-ссылку построить с параметром<v>.

Postconditions:

<this->is_ready()>возвращается<true>.<this->get_state()>возвращается<boost::future_state::ready>.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

Notes:

<get_or()>являетсяточкой прерывания.

void wait() const;

Effects:

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

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

<this->is_ready()>возвращается<true>.<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<wait()>являетсяточкой прерывания.

template<typename Duration>
bool timed_wait(Duration const& wait_duration);
[Warning]Warning

Снят с 3.00.

Вместо этого используйте<wait_for>.

Effects:

Если<*this>связано с общим состоянием, подождите, пока результат будет готов, или время, указанное<wait_duration>, истекло. Если результат не готов при входе, и результат имеет наборожидания обратного вызова, этот обратный вызов вызывается до ожидания.

Returns:

<true>, если<*this>связано с общим состоянием, и этот результат готов до истечения указанного времени,<false>иначе.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

Если этот призыв вернулся<true>, то<this->is_ready()>возвращается<true<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<timed_wait()>являетсяточкой прерывания.<Duration>должен быть типом, который соответствует увеличению. Требования к продолжительности времени.

bool timed_wait(boost::system_time const& wait_timeout);
[Warning]Warning

Снят с 3.00.

Вместо этого используйте<wait_until>.

Effects:

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

Returns:

<true>, если<*this>связано с общим состоянием, и этот результат готов до истечения указанного времени,<false>иначе.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

Если этот призыв вернулся<true>, то<this->is_ready()>возвращается<true<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<timed_wait()>являетсяточкой прерывания.

template <class Rep, class Period>
future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;

Effects:

Если<*this>связано с общим состоянием, подождите, пока результат будет готов, или время, указанное<wait_duration>, истекло. Если результат не готов при входе, и результат имеет наборожидания обратного вызова, этот обратный вызов вызывается до ожидания.

Returns:

3266, если общее состояние содержит отложенную функцию. (пока не реализовано)

-<future_status::ready>, если общее состояние готово.

-<future_status::timeout>если функция возвращается, поскольку истек срок относительного тайм-аута, указанный в<rel_time>.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

Если этот призыв вернулся<true>, то<this->is_ready()>возвращается<true<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<wait_for()>являетсяточкой прерывания.<Duration>должен быть типом, который соответствует Boost. Требования к продолжительности времени.

template <class Clock, class Duration>
future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;

Effects:

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

Returns:

3266, если общее состояние содержит отложенную функцию. (пока не реализовано)

-<future_status::ready>, если общее состояние готово.

-<future_status::timeout>если функция возвращается, потому что абсолютный тайм-аут, указанный в<absl_time>, достиг.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

Если этот призыв вернулся<true>, то<this->is_ready()>возвращается<true<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<wait_until()>являетсяточкой прерывания.

bool valid() const noexcept;

Returns:

<true>, если<*this>связано с общим состоянием,<false>иначе.

Remarks:

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

Throws:

Ничего.

bool is_ready() const;

Returns:

<true>если<*this>связан с общим состоянием и этот результат готов к извлечению,<false>в противном случае.

Remarks:

Результат этой функции нестабилен и что будущее может стать неготовым, даже если функция вернется истинной или наоборот.

Throws:

Ничего.

bool has_value() const;

Returns:

<true>, если<*this>связано с общим состоянием, этот результат готов к извлечению, а результат является сохраненным значением,<false>в противном случае.

Remarks:

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

Throws:

Ничего.

bool has_exception() const;

Returns:

<true>, если<*this>связано с общим состоянием, этот результат готов к извлечению, а результат является сохраненным значением,<false>в противном случае.

Remarks:

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

Throws:

Ничего.

exception_ptr get_exception_ptr();

Effects:

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

Returns:

Исключение_ptr, хранение или не исключение.

Remarks:

Результат этой функции нестабилен и будущее могло бы потерять свое исключение, даже если бы функция вернула действительное<exception_ptr>или наоборот.

Throws:

Все, что<mutex::lock()/mutex::unlock()>может бросить.

future_state::state get_state();

Effects:

Определить состояние общего состояния, связанного с<*this>, если таковое имеется.

Returns:

<boost::future_state::uninitialized>, если<*this>не связан с общим состоянием.<boost::future_state::ready>, если общее состояние, связанное с<*this>, готово к извлечению,<boost::future_state::waiting>в противном случае.

Remarks:

Результат этой функции не стабилен.

Throws:

Ничего.

shared_future<R> share();

Returns:

<shared_future<R>(boost::move(*this))>.

Postconditions:

<this->valid() ==false>.

template<typename F>
 future<typename boost::result_of<F( future)>::type>then(F&& func); // EXTENSIONtemplate<typename S, typename F>
 future<typename boost::result_of<F( future)>::type>then(Ex& executor, F&& func); // EXTENSIONtemplate<typename F>
 future<typename boost::result_of<F( future)>::type>then(launch policy, F&& func); // EXTENSION
[Warning]Warning

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

[Note]Note

Эти функции основаны на. N3634 - Улучшения для std::future< T>и связанные с ним APIC++1y предложены Н. Густафссоном, А. Лаксбергом, Х. Саттером, С. Митани.

Notes:

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

Requires:

<INVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this))>является действительным выражением.

Effects:

Все функции создают общее состояние, связанное с возвращаемым будущим объектом. Кроме того,

Когда общее состояние объекта готово, продолжение<INVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this))>вызывается в зависимости от перегрузки (см. ниже), а вызов DECAY_COPY () оценивается в потоке, который вызвал тогда.

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

Продолжение запускается по указанному полису или исполнителю или отметке.

Когда политика запуска<launch::none>, продолжение называется неопределенной нитью исполнения.

Когда политика запуска<launch::async>, продолжение называется новой нитью исполнения.

Когда политика запуска<launch::deferred>, продолжение вызывается по требованию.

- При запуске полиса<launch::executor>продолжение вызывается на одну из ниток исполнения исполнителя.

Когда политика запуска является<launch::inherit>продолжение наследует политику запуска родителя или исполнителя.

Когда исполнитель или политика запуска не предусмотрена (первая перегрузка) как будто запуск:: не уточняется.

- При предоставлении исполнителя (вторая перегрузка) продолжение вызывается на одну из ниток исполнения исполнителя.

Если у родителя есть политика<launch::deferred>и продолжение не имеет определенного исполнителя политики запуска, то родитель заполняется немедленно позвонив<.wait()>, а политика предшествующего —<launch::deferred>.

Returns:

Объект типа<future><имени типаповышения::результата_<F<future>]>, который относится к общему состоянию, созданному продолжением.

Notes:

Обратите внимание, что вложенные фьючерсы еще не скрыты. Это может быть изменено в будущих версиях.

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

Postconditions:

- Объект<future>, переданный параметру функции продолжения, является копией оригинала<future>.

<valid() ==false>о первоначальном будущем;<valid()== true>о<future>возвращенном оттуда.

template <typename R2>
 future<R2>  future< future<R2>>::unwrap();  // EXTENSIONtemplate <typename R2>
 boost::shared_future<R2>  future< boost::shared_future<R2>>::unwrap();  // EXTENSION
[Warning]Warning

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

[Note]Note

Эти функции основаны на. N3634 - Улучшения для std::future< T>и связанные с ним APIC++1y предложены Н. Густафссоном, А. Лаксбергом, Х. Саттером, С. Митани.

Notes:

Удаляет внешнее будущее и возвращает будущее с ассоциированным состоянием, являющимся прокси внешнего будущего.

Effects:

Возвращает будущее, которое становится готовым, когда общее состояние внешнего и внутреннего будущего готово. Действительность будущего, возвращаемого из<get()>, примененного к внешнему будущему, не может быть установлена a priori. Если оно недействительно, то это будущее вынуждено быть действительным и становится готовым за исключением типа<future_error>, с кодом ошибки<future_errc::broken_promise>.

Returns:

Объект типа будущего с ассоциированным состоянием был прокси внешнего будущего.

Postconditions:

Возвращенное будущее<valid()== true>.

template <typename R>class shared_future{public:
  typedef future_state::state state; // EXTENSION
  typedef R value_type;  // EXTENSION
  shared_future() noexcept;
  ~shared_future();
  // copy support
  shared_future(shared_future const& other);
  shared_future& operator=(shared_future const& other);
  // move support
  shared_future(shared_future && other) noexcept;
  shared_future( future<R> && other) noexcept;
  shared_future& operator=(shared_future && other) noexcept;
  shared_future& operator=( future<R> && other) noexcept;
  // factories
  template<typename F>
   future<typename boost::result_of<F(shared_future)>::type>
  then(F&& func) const; // EXTENSION
  template<typename S, typename F>
   future<typename boost::result_of<F(shared_future)>::type>
  then(S& scheduler, F&& func) const; // EXTENSION
  template<typename F>
   future<typename boost::result_of<F(shared_future)>::type>
  then(launch policy, F&& func) const; // EXTENSION
  void swap(shared_future& other);
  // retrieving the value
  see below get() const;
  exception_ptr get_exception_ptr(); // EXTENSION
  // functions to check state, and wait for ready
  bool valid() const noexcept;
  bool is_ready() const noexcept; // EXTENSION
  bool has_exception() const noexcept; // EXTENSION
  bool has_value() const noexcept; // EXTENSION
  // waiting for the result to be ready
  void wait() const;
  template <class Rep, class Period>
  future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
  template <class Clock, class Duration>
  future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
#if defined BOOST_THREAD_USES_DATE_TIME || defined BOOST_THREAD_DONT_USE_CHRONO
  template<typename Duration>
  bool timed_wait(Duration const& rel_time) const;  // DEPRECATED SINCE V3.0.0
  bool timed_wait_until(boost::system_time const& abs_time) const;  // DEPRECATED SINCE V3.0.0#endif
  state get_state() const noexcept;  // EXTENSION
};
shared_future();

Effects:

Построение неинициализированного совместного будущего.

Postconditions:

<this->is_ready>возвращается<false>.<this->get_state()>возвращается<boost::future_state::uninitialized>.

Throws:

Ничего.

const R& get() const;
R& get() const;
void get() const;

Effects:

Если<*this>связано с общим состоянием, ожидайте, пока результат не будет готов, как-если по вызову<boost::shared_future<R>::wait()>, и возвращайте<const>ссылку на результат.

Returns:

<shared_future<R&>::get()>вернуть сохраненную ссылку.

-<shared_future<void>::get()>, обратной стоимости нет.

<shared_future<R>::get()>возвращает<const>ссылку на значение, сохраненное в общем состоянии.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Notes:

<get()>являетсяточкой прерывания.

void wait() const;

Effects:

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

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

<this->is_ready()>возвращается<true>.<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<wait()>являетсяточкой прерывания.

template<typename Duration>
bool timed_wait(Duration const& wait_duration);

Effects:

Если<*this>связано с общим состоянием, подождите, пока результат будет готов, или время, указанное<wait_duration>, истекло. Если результат не готов при входе, и результат имеет наборожидания обратного вызова, этот обратный вызов вызывается до ожидания.

Returns:

<true>, если<*this>связано с общим состоянием, и этот результат готов до истечения указанного времени,<false>иначе.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

Если этот звонок вернулся<true>, то<this->is_ready()>возвращается<true<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<timed_wait()>являетсяточкой прерывания.<Duration>должен быть типом, который соответствует увеличению. Требования к продолжительности времени.

bool timed_wait(boost::system_time const& wait_timeout);

Effects:

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

Returns:

<true>, если<*this>связано с общим состоянием, и этот результат готов до истечения указанного времени,<false>иначе.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

Если этот звонок вернулся<true>, то<this->is_ready()>возвращается<true<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<timed_wait()>являетсяточкой прерывания.

template <class Rep, class Period>
future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;

Effects:

Если<*this>связано с общим состоянием, подождите, пока результат будет готов, или время, указанное<wait_duration>, истекло. Если результат не готов при входе, и результат имеет наборожидания обратного вызова, этот обратный вызов вызывается до ожидания.

Returns:

3266, если общее состояние содержит отложенную функцию. (пока не реализовано)

-<future_status::ready>, если общее состояние готово.

-<future_status::timeout>если функция возвращается, поскольку истек срок относительного тайм-аута, указанный в<rel_time>.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

Если этот звонок вернулся<true>, то<this->is_ready()>возвращается<true<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<timed_wait()>являетсяточкой прерывания.<Duration>должен быть типом, который соответствует увеличению. Требования к продолжительности времени.

template <class Clock, class Duration>
future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;

Effects:

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

Returns:

3266, если общее состояние содержит отложенную функцию. (пока не реализовано)

-<future_status::ready>, если общее состояние готово.

-<future_status::timeout>если функция возвращается, потому что абсолютный тайм-аут, указанный в<absl_time>, достиг.

Throws:

<boost::future_uninitialized>, если<*this>не связано с общим состоянием.

-<boost::thread_interrupted>если результат, связанный с<*this>, не готов в точке вызова, и текущая нить прерывается.

Любое исключение, сделанноеожиданием обратного вызова, если такой обратный вызов вызван.

Postconditions:

Если этот звонок вернулся<true>, то<this->is_ready()>возвращается<true<this->get_state()>возвращается<boost::future_state::ready>.

Notes:

<timed_wait()>являетсяточкой прерывания.

bool valid() const noexcept;

Returns:

<true>, если<*this>связано с общим состоянием,<false>иначе.

Throws:

Ничего.

bool is_ready() const;

Returns:

<true>если<*this>связан с общим состоянием и этот результат готов к извлечению,<false>в противном случае.

Throws:

Все, что<mutex::lock()/mutex::unlock()>может бросить.

bool has_value() const;

Returns:

<true>, если<*this>связано с общим состоянием, этот результат готов к извлечению, а результат является сохраненным значением,<false>в противном случае.

Throws:

Все, что<mutex::lock()/mutex::unlock()>может бросить.

bool has_exception() const;

Returns:

<true>, если<*this>связано с общим состоянием, этот результат готов к извлечению, а результат является сохраненным значением,<false>в противном случае.

Throws:

Все, что<mutex::lock()/mutex::unlock()>может бросить.

exception_ptr get_exception_ptr();

Effects:

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

Returns:

Исключение_ptr, хранение или не исключение.

Throws:

Все, что<mutex::lock()/mutex::unlock()>может бросить.

future_state::state get_state();

Effects:

Определить состояние общего состояния, связанного с<*this>, если таковое имеется.

Returns:

<boost::future_state::uninitialized>, если<*this>не связан с общим состоянием.<boost::future_state::ready>, если общее состояние, связанное с<*this>, готово к извлечению,<boost::future_state::waiting>в противном случае.

Throws:

Все, что<mutex::lock()/mutex::unlock()>может бросить.

template<typename F>
 future<typename boost::result_of<F(shared_future)>::type>then(F&& func) const; // EXTENSIONtemplate<typename S, typename F>
 future<typename boost::result_of<F(shared_future)>::type>then(Ex& executor, F&& func) const; // EXTENSIONtemplate<typename F>
 future<typename boost::result_of<F(shared_future)>::type>then(launch policy, F&& func) const; // EXTENSION
[Warning]Warning

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

[Note]Note

Эти функции основаны на. N3634 - Улучшения для std::future< T>и связанные с ним APIC++1y предложены Н. Густафссоном, А. Лаксбергом, Х. Саттером, С. Митани.

Notes:

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

Requires:

<INVOKE(DECAY_COPY(std::forward<F>(func)), *this)>является действительным выражением.

Effects:

Все функции создают общее состояние, связанное с возвращаемым будущим объектом. Кроме того,

Когда общее состояние объекта готово, продолжение<INVOKE(DECAY_COPY(std::forward<F>(func)), *this)>вызывается в зависимости от перегрузки (см. ниже), а вызов DECAY_COPY () оценивается в потоке, который вызвал тогда.

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

Продолжение запускается по указанному полису или исполнителю или отметке.

Когда политика запуска<launch::none>, продолжение называется неопределенной нитью исполнения.

Когда политика запуска<launch::async>, продолжение называется новой нитью исполнения.

Когда политика запуска<launch::deferred>, продолжение вызывается по требованию.

- При запуске полиса<launch::executor>продолжение вызывается на одну из ниток исполнения исполнителя.

Когда политика запуска является<launch::inherit>продолжение наследует политику запуска родителя или исполнителя.

Когда исполнитель или политика запуска не предусмотрена (первая перегрузка) как будто запуск:: не уточняется.

- При предоставлении исполнителя (вторая перегрузка) продолжение вызывается на одну из ниток исполнения исполнителя.

Если у родителя есть политика<launch::deferred>и продолжение не имеет определенного исполнителя политики запуска, то родитель заполняется немедленно позвонив<.wait()>, а политика предшествующего —<launch::deferred>.

Returns:

Объект типа<future><имени типаповышения::результата_<Fсовместного_будущего>, который относится к общему состоянию, созданному продолжением.

Notes:

Обратите внимание, что вложенные фьючерсы еще не скрыты. Это может быть изменено в будущих версиях.

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

Postconditions:

Будущий объект перемещается к параметру функции продолжения.

<valid() ==true>на оригинале<shared_future>;<valid() ==true>на<future>возвращенном с того времени.

template <typename R>class promise{public:
  typedef R value_type;  // EXTENSION
  promise();
  template <class Allocator>
  promise(allocator_arg_t, Allocator a);
  promise & operator=(promise const& rhs) = delete;
  promise(promise const& rhs) = delete;
  ~promise();
  // Move support
  promise(promise && rhs) noexcept;;
  promise & operator=(promise&& rhs) noexcept;;
  void swap(promise& other) noexcept;
  // Result retrieval
   future<R> get_future();
  // Set the value
  void set_value(see below);
  void set_exception(boost::exception_ptr e);
  template <typename E>
  void set_exception(E e); // EXTENSION
  // setting the result with deferred notification
  void set_value_at_thread_exit(see below);
  void set_exception_at_thread_exit(exception_ptr p);
  template <typename E>
  void set_exception_at_thread_exit(E p);  // EXTENSION
  template<typename F>
  void set_wait_callback(F f); // EXTENSION};
promise();

Effects:

Построение нового<boost::promise>без соответствующего результата.

Throws:

Ничего.

template <class Allocator>
promise(allocator_arg_t, Allocator a);

Effects:

Конструирует новый<boost::promise>без соответствующего результата с помощью распределителя<a>.

Throws:

Ничего.

Notes:

Доступно только при определении BOOST_THREAD_FUTURE_USES_ALLOCATORS.

promise(promise && other);

Effects:

Конструирует новый<boost::promise>и передает право собственности на результат, связанный с<other>,<*this>, оставляя<other>без связанного результата.

Throws:

Ничего.

Notes:

Если компилятор не поддерживает rvalue-ссылки, это реализуется с помощью boost. Эмуляция движения резьбы.

promise& operator=(promise && other);

Effects:

Переносит право собственности на результат, связанный с<other>на<*this>, оставляя<other>без связанного результата. Если уже был результат, связанный с<*this>, и этот результат не былготовым, то любое будущее, связанное с этим результатом, устанавливаетготовымс исключением<boost::broken_promise>в результате.

Throws:

Ничего.

Notes:

Если компилятор не поддерживает rvalue-ссылки, это реализуется с помощью boost. Эмуляция движения резьбы.

~promise();

Effects:

Уничтожает<*this>. Если был результат, связанный с<*this>, и этот результат не готов, устанавливает любые фьючерсы, связанные с этой задачей, нас исключением<boost::broken_promise>в результате.

Throws:

Ничего.

 future<R> get_future();

Effects:

Если<*this>не был связан с результатом, выделяйте хранилище для нового общего состояния и связывайте его с<*this>. Возвращает<future>, связанный с результатом<*this>.

Throws:

3737, если будущее, связанное с этой задачей, уже восстановлено.<std::bad_alloc>, если какая-либо необходимая память не может быть выделена.

void set_value(R&& r);
void set_value(const R& r);
void promise<R&>::set_value(R& r);
void promise<void>::set_value();

Effects:

Если BOOST_THREAD_PROVIDES_PROMISE_LAZY определен и если<*this>не был связан с результатом, выделите хранилище для нового общего состояния и свяжите его с<*this>.

Храните значение<r>в общем состоянии, связанном с<*this>. Любые потоки, заблокированные в ожидании асинхронного результата, пробуждаются.

Postconditions:

Все фьючерсы, ожидающие общего состояния,готовыи<boost::future<R>::has_value()>или<boost::shared_future<R>::has_value()>для этих фьючерсов должны вернуться<true>.

Throws:

<boost::promise_already_satisfied>, если результат, связанный с<*this>, ужеготов.

<boost::broken_promise>, если<*this>не имеет общего состояния.

<std::bad_alloc>если память, необходимая для хранения результата, не может быть выделена.

Любое исключение, сделанное копией или конструктором движения<R>.

void set_exception(boost::exception_ptr e);
template <typename E>
void set_exception(E e); // EXTENSION

Effects:

Если BOOST_THREAD_PROVIDES_PROMISE_LAZY определен и если<*this>не был связан с результатом, выделите хранилище для нового общего состояния и свяжите его с<*this>.

Сохранить исключение<e>в общем состоянии, связанном с<*this>. Любые потоки, заблокированные в ожидании асинхронного результата, пробуждаются.

Postconditions:

Все фьючерсы, ожидающие совместного состояния,готовыи<boost::future<R>::has_exception()>или<boost::shared_future<R>::has_exception()>для этих фьючерсов должны вернуться<true>.

Throws:

<boost::promise_already_satisfied>, если результат, связанный с<*this>, ужеготов.

<boost::broken_promise>, если<*this>не имеет общего состояния.

<std::bad_alloc>если память, необходимая для хранения результата, не может быть выделена.

void set_value_at_thread_exit(R&& r);
void set_value_at_thread_exit(const R& r);
void promise<R&>::set_value_at_thread_exit(R& r);
void promise<void>::set_value_at_thread_exit();

Effects:

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

Throws:

<boost::promise_already_satisfied>, если результат, связанный с<*this>, ужеготов.

<boost::broken_promise>, если<*this>не имеет общего состояния.

<std::bad_alloc>если память, необходимая для хранения результата, не может быть выделена.

Любое исключение, сделанное копией или конструктором движения<R>.

void set_exception_at_thread_exit(boost::exception_ptr e);
template <typename E>
void set_exception_at_thread_exit(E p);  // EXTENSION

Effects:

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

Postconditions:

Все фьючерсы, ожидающие совместного состояния,готовыи<boost::future<R>::has_exception()>или<boost::shared_future<R>::has_exception()>для этих фьючерсов должны вернуться<true>.

Throws:

<boost::promise_already_satisfied>, если результат, связанный с<*this>, ужеготов.

<boost::broken_promise>, если<*this>не имеет общего состояния.

<std::bad_alloc>если память, необходимая для хранения результата, не может быть выделена.

template<typename F>
void set_wait_callback(F f);

Preconditions:

Выражение<f(t)>, где<t>является значением l типа<boost::promise>, должно быть хорошо сформировано. Ссылка на копию<f>имеет тот же эффект, что и ссылка<f>.

Effects:

Храните копию<f>с общим состоянием, связанным с<*this>какожидание обратного вызова. Это заменит любой существующий магазин обратного вызова ожидания вместе с этим результатом. Если нить впоследствии вызывает одну из функций ожидания на<future>или<boost::shared_future>, связанных с этим результатом, и результат не готов,<f(*this)>должен быть вызван.

Throws:

<std::bad_alloc>если память не может быть выделена для требуемого хранения.

template<typename S>class packaged_task;template<typename R
  , class... ArgTypes>class packaged_task<R(ArgTypes)>{public:
  packaged_task(packaged_task const&) = delete;
  packaged_task& operator=(packaged_task const&) = delete;
  // construction and destruction
  packaged_task() noexcept;
  explicit packaged_task(R(*f)(ArgTypes...));
  template <class F>
  explicit packaged_task(F&& f);
  template <class Allocator>
  packaged_task(allocator_arg_t, Allocator a, R(*f)(ArgTypes...));
  template <class F, class Allocator>
  packaged_task(allocator_arg_t, Allocator a, F&& f);
  ~packaged_task()
  {}
  // move support
  packaged_task(packaged_task&& other) noexcept;
  packaged_task& operator=(packaged_task&& other) noexcept;
  void swap(packaged_task& other) noexcept;
  bool valid() const noexcept;
  // result retrieval
   future<R> get_future();
  // execution
  void operator()(ArgTypes... );
  void make_ready_at_thread_exit(ArgTypes...);
  void reset();
  template<typename F>
  void set_wait_callback(F f);  // EXTENSION};
packaged_task(R(*f)(ArgTypes...));
template<typename F>
packaged_task(F&&f);

Preconditions:

<f()>является действительным выражением с обратным типом, конвертируемым в<R>. Вызов копии<f>должен вести себя так же, как вызов<f>.

Effects:

Построен новый<boost::packaged_task>с<boost::forward<F>(f)>сохраненным в качестве связанной задачи.

Throws:

Любые исключения, выброшенные копией (или ходом) конструктора<f>.

<std::bad_alloc>, если память для внутренних структур данных не может быть выделена.

Notes:

Перегрузка R(*f)(ArgTypes...) для пропускания функции без необходимости использования<&>.

Remark:

Этот конструктор не участвует в разрешении перегрузки, если decay::type является тем же типом, что и boost::packaged_task.

template <class Allocator>
packaged_task(allocator_arg_t, Allocator a, R(*f)(ArgTypes...));
template <class F, class Allocator>
packaged_task(allocator_arg_t, Allocator a, F&& f);

Preconditions:

<f()>является действительным выражением с обратным типом, конвертируемым в<R>. Копия<f>должна вести себя так же, как и призыв<f>.

Effects:

Постраивает новый<boost::packaged_task>с<boost::forward<F>(f)>сохраненным в качестве связанной задачи с использованием распределителя<a>.

Throws:

Любые исключения, брошенные конструктором копирования (или перемещения)<f><std::bad_alloc>, если память для внутренних структур данных не могла быть выделена.

Notes:

Доступно только при определении BOOST_THREAD_FUTURE_USES_ALLOCATORS.

Notes:

Перегрузка R(*f)(ArgTypes...) для пропускания функции без необходимости использования<&>.

packaged_task(packaged_task && other);

Effects:

Конструирует новый<boost::packaged_task>и передает право собственности на задачу, связанную с<other>,<*this>, оставляя<other>без связанной задачи.

Throws:

Ничего.

Notes:

Если компилятор не поддерживает rvalue-ссылки, это реализуется с помощью boost. Эмуляция движения резьбы.

packaged_task& operator=(packaged_task && other);

Effects:

Переносит право собственности на задание, связанное с<other>на<*this>, оставляя<other>без связанного задания. Если уже была задача, связанная с<*this>, и эта задача не была вызвана, устанавливает любые фьючерсы, связанные с этой задачей, наготовыес<boost::broken_promise>исключением в результате.

Throws:

Ничего.

Notes:

Если компилятор не поддерживает rvalue-ссылки, это реализуется с помощью boost. Эмуляция движения резьбы.

~packaged_task();

Effects:

Уничтожает<*this>. Если была задача, связанная с<*this>, и эта задача не была вызвана, устанавливает любые фьючерсы, связанные с этой задачей, наготовыйс исключением<boost::broken_promise>в результате.

Throws:

Ничего.

 future<R> get_future();

Effects:

Возвращает<future>, связанный с результатом задачи, связанной с<*this>.

Throws:

<boost::task_moved>, если право собственности на связанное с<*this>задание было перенесено на другой экземпляр<boost::packaged_task><boost::future_already_retrieved>, если будущее, связанное с этим заданием, уже восстановлено.

void operator()();

Effects:

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

Postconditions:

Все фьючерсы, ожидающие общего состоянияготовы

Throws:

<boost::task_moved>, если право собственности на задание, связанное с<*this>, было перенесено на другой экземпляр<boost::packaged_task>.

-<boost::task_already_started>, если задача уже выполнена.

void make_ready_at_thread_exit(ArgTypes...);

Effects:

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

Throws:

<boost::task_moved>, если право собственности на задание, связанное с<*this>, было перенесено на другой экземпляр<boost::packaged_task>.

-<boost::task_already_started>, если задача уже выполнена.

void reset();

Effects:

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

Throws:

<boost::task_moved>, если право собственности на задание, связанное с<*this>, было перенесено на другой экземпляр<boost::packaged_task>.

template<typename F>
void set_wait_callback(F f);

Preconditions:

Выражение<f(t)>, где<t>является значением l типа<boost::packaged_task>, должно быть хорошо сформировано. Призыв копии<f>имеет тот же эффект, что и призыв<f>.

Effects:

Сохраните копию<f>с задачей, связанной с<*this>какожидание обратного вызова. Это заменит любой существующий магазин обратного вызова ожидания вместе с этой задачей. Если нить впоследствии вызывает одну из функций ожидания на<future>или<boost::shared_future>, связанных с этой задачей, и результат задачи не готов,<f(*this)>должен быть вызван.

Throws:

<boost::task_moved>, если право собственности на задание, связанное с<*this>, было перенесено на другой экземпляр<boost::packaged_task>.

template <class T>
typename decay<T>::type decay_copy(T&& v)
{
  return boost::forward<T>(v);
}

Шаблон функции async обеспечивает механизм запуска функции потенциально в новом потоке и обеспечивает результат функции в будущем объекте, с которым он разделяет общее состояние.

Non-Variadic variant
template <class F>
   future<typename result_of<typename decay<F>::type()>::type>
  async(F&& f);template <class F>
   future<typename result_of<typename decay<F>::type()>::type>
  async(launch policy, F&& f);template <class Executor, class F>
   future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
  async(Executor &ex, F&& f, Args&&... args);

Requires:

decay_copy(boost::forward<F>(f))()

Это должно быть действительное выражение.

Effects

Первая функция ведет себя так же, как призыв ко второй функции с политическим аргументом<launch::async |launch::deferred>и теми же аргументами для<F>.

Вторая и третья функции создают общее состояние, связанное с возвращаемым будущим объектом.

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

- если<policy& launch::async>ненулевой - вызовы<decay_copy(boost::forward<F>(f))()>как бы в новой нити исполнения, представленной резьбовым объектом, причем вызовы к<decay_copy()>оцениваются в нити, которая вызвала<async>. Любая обратная стоимость сохраняется в результате в общем состоянии. Любое исключение из исполнения<decay_copy(boost::forward<F>(f))()>сохраняется как исключительный результат в общем состоянии. Объект резьбы хранится в общем состоянии и влияет на поведение любых асинхронных объектов возврата, которые ссылаются на это состояние.

Если<policy& launch::deferred>ненулевой — Магазины<decay_copy(boost::forward<F>(f))>в общем состоянии. Эта копия<f>представляет собой отложенную функцию. Вызов отложенной функции оценивает<boost::move(g)()>, где<g>является сохраненным значением<decay_copy(boost::forward<F>(f))>. Совместное состояние не готово до тех пор, пока функция не будет завершена. Первый вызов несвоевременной функции ожидания на асинхронном объекте возврата, относящемся к этому общему состоянию, вызывает отложенную функцию в потоке, которая называется функцией ожидания. Как только начинается оценка<boost::move(g)()>, функция больше не считается отложенной. (Примечание: Если эта политика указана вместе с другими политиками, например, при использовании значения политики<launch::async |launch::deferred>, реализация должна отложить вызов или выбор политики, когда больше не может быть эффективно использовано.)

- если нет действующей политики запуска, поведение не определено.

Дальнейшее поведение третьей функции выглядит следующим образом:

Исполнителю::submit() функция дается функциякоторая называется 'INVOKE (DECAY_COPY (std::forward(f)), DECAY_COPY (std::forward(args)).... Реализация исполнителя решается программистом.

Returns:

Объект типа<future><typenameresult_of<typenamedecay<F>::type[)>::type>, который относится к общему состоянию, создаваемому этим вызовом<async>.

Synchronization:

Независимо от политических аргументов,

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

- завершение функции<f>секвенируется до того, как общее состояние будет подготовлено. (Примечание:<f>может вообще не называться, поэтому его завершение может никогда не произойти.)

Если вы выбираете политику<launch::async>,

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

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

Throws:

<system_error>если политика<launch::async>и реализация не в состоянии запустить новую нить.

Error conditions:

-<resource_unavailable_try_again>- если политика<launch::async>и система не в состоянии запустить новую нить.

Remarks::

Первая подпись не должна участвовать в разрешении перегрузки, если<decay_t<F> is>увеличат:: запуск< or>увеличат::is_executor< is>true_type'.

Variadic variant
template <class F, class... Args>
   future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
  async(F&& f, Args&&... args);template <class F, class... Args>
   future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
  async(launch policy, F&& f, Args&&... args);template <class Executor, class F, class... Args>
   future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
  async(Executor &ex, F&& f, Args&&... args);
[Warning]Warning

Вариадный прототип предоставляется только на компиляторах C++11, поддерживающих rvalue-ссылки, вариадные шаблоны, деклатип и стандартную библиотеку, обеспечивающую(в ожидании повышения::tuple, который движется осознанно), и определяется BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK.

Requires:

<F>и каждый<Ti>в<Args>должны удовлетворять<MoveConstructible>требованиям.

invoke (decay_copy (boost::forward(f)), decay_copy (boost::forward(args))...)

Это должно быть действительное выражение.

Effects:

Первая функция ведет себя так же, как призыв ко второй функции с политическим аргументом<launch::async |launch::deferred>и теми же аргументами для<F>и<Args>.

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

- если<policy& launch::async>ненулевой - вызовы<invoke(decay_copy(forward<F>(f)), decay_copy(forward<Args>(args))...)>как бы в новой нити исполнения, представленной объектом нити, причем вызовы<decay_copy()>оцениваются в нити, которая вызвала<async>. Любая обратная стоимость сохраняется в результате в общем состоянии. Любое исключение из исполнения<invoke(decay_copy(boost::forward<F>(f)),decay_copy (boost::forward<Args>(args))...)>сохраняется как исключительный результат в общем состоянии. Объект резьбы хранится в общем состоянии и влияет на поведение любых асинхронных объектов возврата, которые ссылаются на это состояние.

- если<policy& launch::deferred>ненулевой - Магазины<decay_copy(forward<F>(f))>и<decay_copy(forward<Args>(args))...>в общем состоянии. Эти копии<f>и<args>представляют собой отложенную функцию. Вызов отложенной функции оценивает<invoke(move(g), move(xyz))>, где<g>является сохраненным значением<decay_copy(forward<F>(f))>и<xyz>является сохраненной копией<decay_copy(forward<Args>(args))...>. Совместное состояние не готово до тех пор, пока функция не будет завершена. Первый вызов несвоевременной функции ожидания на асинхронном объекте возврата, относящемся к этому общему состоянию, вызывает отложенную функцию в потоке, которая называется функцией ожидания. Как только начинается оценка<invoke(move(g), move(xyz))>, функция больше не считается отложенной.

- если нет действующей политики запуска, поведение не определено.

Note:

Если эта политика определена вместе с другими политиками, например, при использовании значения политики<launch::async |launch::deferred>, реализация должна отложить вызов или выбор политики, когда больше не может быть эффективно использовано.

Returns:

Объект типа<future><, результат_<, тип, распад<F>:, тип, тип, тип, распад,,и [gt]::, тип, тип, тип,, который относится к общему состоянию, создаваемому этим вызовом<async>.

Synchronization:

Независимо от политических аргументов,

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

- завершение функции<f>секвенируется до того, как общее состояние будет подготовлено. (Примечание: f может вообще не называться, поэтому его завершение может никогда не произойти.)

Если вы выбираете политику<launch::async>,

- вызов функции ожидания на асинхронном объекте возврата, который разделяет совместное состояние, созданное этим вызовом асинхронизации, должен блокироваться до тех пор, пока связанный поток не завершится, как если бы он был присоединен, или же не закончится;

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

Throws:

<system_error>если политика<launch::async>и реализация не в состоянии запустить новую нить.

Error conditions:

-<resource_unavailable_try_again>- если политика<launch::async>и система не в состоянии запустить новую нить.

Remarks:

Первая подпись не должна участвовать в разрешении перегрузки, если распад::type is boost::launch.

template<typename Iterator>
  Iterator wait_for_any(Iterator begin,Iterator end); // EXTENSION
template<typename F1,typename F2>
  unsigned wait_for_any(F1& f1,F2& f2); // EXTENSION
template<typename F1,typename F2,typename F3>
  unsigned wait_for_any(F1& f1,F2& f2,F3& f3); // EXTENSION
template<typename F1,typename F2,typename F3,typename F4>
  unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4); // EXTENSION
template<typename F1,typename F2,typename F3,typename F4,typename F5>
  unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5); // EXTENSION

Preconditions:

Типы<Fn>должны быть специализацией<future>или<boost::shared_future>, а<Iterator>должен быть передним итератором с<value_type>, который является специализацией<future>или<boost::shared_future>.

Effects:

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

Returns:

Перегрузка на основе диапазона возвращает<Iterator>, идентифицируя первое будущее в диапазоне, который был обнаружен какготовый. Остальные перегрузки возвращают нулевой индекс первого будущего, который был обнаружен какготовый(первый параметр =>0, второй параметр =>1, и т.д.).

Throws:

<boost::thread_interrupted>при прерывании текущей нити. Любое исключение, брошенноеожиданием обратного вызова, связанное с любым из будущих ожиданий.<std::bad_alloc>, если память не может быть выделена для внутренних структур ожидания.

Notes:

<wait_for_any()>являетсяточкой прерывания.

template<typename Iterator>
  void wait_for_all(Iterator begin,Iterator end); // EXTENSION
template<typename F1,typename F2>
  void wait_for_all(F1& f1,F2& f2); // EXTENSION
template<typename F1,typename F2,typename F3>
  void wait_for_all(F1& f1,F2& f2,F3& f3); // EXTENSION
template<typename F1,typename F2,typename F3,typename F4>
  void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4); // EXTENSION
template<typename F1,typename F2,typename F3,typename F4,typename F5>
  void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5); // EXTENSION

Preconditions:

Типы<Fn>должны быть специализацией<future>или<boost::shared_future>, а<Iterator>должен быть передним итератором с<value_type>, который является специализацией<future>или<boost::shared_future>.

Effects:

Подождет, пока все указанные фьючерсы будут готовы.

Throws:

Любые исключения, вызванные вызовом<wait()>по указанным фьючерсам.

Notes:

<wait_for_all()>являетсяточкой прерывания.

template <class InputIterator>
  future<std::vector<typename InputIterator::value_type::value_type>>
  when_all(InputIterator first, InputIterator last);
template <typename... FutTypes>
  future<std::tuple<decay_t<FutTypes>...> when_all(FutTypes&&... futures);

Requires:

- При первой перегрузке<InputIterator>тип значения должен быть конвертируемым в<future<R>>или<shared_future<R>>. Все типы<R>должны быть одинаковыми. Если какой-либо из объектов<future<R>>или<shared_future<R>>находится в недействительном состоянии (то есть<valid() ==false>), поведение не определено. Для второй перегрузки<FutTypes>имеет тип<future<R>>или<shared_future<R>>. Воздействие вызова<when_all>на<future>или<shared_future>объект, для которого<valid()== false>не определено.

Notes:

Есть два варианта<when_all>. Первый вариант занимает пару<InputIterators>. Во-вторых, любое произвольное число<future<R0>>и<shared_future<R1>>объектов, где<R0>и<R1>не обязательно должны быть одного и того же типа.

Называя первую подпись<when_all>, где<InputIterator>первая равна последней, возвращает будущее с пустым<vector>, которое немедленно готово.

Называя вторую подпись<when_all>без каких-либо аргументов, возвращается будущее, которое немедленно готово.

Effects:

Если какое-либо из фьючерсов, предоставленных для вызова<when_all>, относится к отложенным задачам, которые не начали выполняться, эти задачи выполняются до возврата вызова<when_all>. Как только все такие задачи выполнены, звонок в<when_all>немедленно возвращается.

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

После того, как все<future>s/<shared_future>s, поставляемые на вызов<when_all>, готовы,<future>s/<shared_future>s перемещаются/копируются в связанное состояние будущего, возвращенное с вызова на<when_all>, сохраняя порядок фьючерсов, поставляемых на<when_all>.

Коллекция затем хранится в результате вновь созданного общего состояния.

Создается новый объект будущего, который относится к общему состоянию. Точный тип будущего описан ниже.

<future>, возвращенный<when_all>, не станет исключением при вызове<wait()>или<get()>, но фьючерсы, хранящиеся в выходной коллекции, могут.

Returns:

<future<tuple<>>>, если<when_all>вызывается с нулевыми аргументами.

-<future<vector<future<R>>>>, если при компиляции неизвестна входная кардинальность и пара итератора дает<future<R>>. Порядок фьючерсов в выходном векторе будет таким же, как данный входным итератором.

-<future<vector<shared_future<R>>>>, если входная кардинальность неизвестна во время компиляции и пара итератора дает<shared_future<R>>. Порядок фьючерсов в выходном векторе будет таким же, как данный входным итератором.

-<future<tuple<decay_t<FutTypes>...>>>, если входы фиксированы в количестве.

Postconditions:

Все входные фьючерсы действительны() == ложны.

- Все вводимые данные являются действительными () == истинными.

- действительный() == истинный.

template <class InputIterator>
  future<std::vector<typename InputIterator::value_type::value_type>>
  when_any(InputIterator first, InputIterator last);
template <typename... FutTypes>
  future<std::tuple<decay_t<FutTypes>...>
  when_any(FutTypes&&... futures);

Requires:

- При первой перегрузке<InputIterator>тип значения должен быть конвертируемым в<future<R>>или<shared_future<R>>. Все типы<R>должны быть одинаковыми. Если какой-либо из объектов<future<R>>или<shared_future<R>>находится в недействительном состоянии (то есть<valid() ==false>), поведение не определено. Для второй перегрузки<FutTypes>имеет тип<future<R>>или<shared_future<R>>. Воздействие вызова<when_any>на<future>или<shared_future>объект, для которого<valid()== falseis undefined>.

Notes:

Есть два варианта<when_any >. Первая версия занимает пару<InputIterators>. Второе — произвольное число объектов<future<R0>>и<shared_future<R1>>, где<R0>и<R1>не обязательно должны быть одного типа.

Называя первую подпись<when_any >, где<InputIterator>первая равна последней, возвращает будущее с пустым<vector>, которое немедленно готово.

Называя вторую подпись<when_any>без аргументов, возвращает будущее, которое немедленно готово.

Effects:

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

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

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

Коллекция затем хранится в результате вновь созданного общего состояния.

Создается новый объект будущего, который относится к общему состоянию. Точный тип будущего описан ниже.

Будущее, возвращенное<when_any>, не станет исключением при вызове<wait()>или<get()>, но фьючерсы, хранящиеся в выходной коллекции, могут.

Returns:

<future<tuple<>>>, если<when_any>вызывается с нулевыми аргументами.

-<future<vector<future<R>>>>, если при компиляции неизвестна входная кардинальность и пара итератора дает<future<R>>. Порядок фьючерсов в выходном векторе будет таким же, как данный входным итератором.

-<future<vector<shared_future<R>>>>, если входная кардинальность неизвестна во время компиляции и пара итератора дает<shared_future<R>>. Порядок фьючерсов в выходном векторе будет таким же, как данный входным итератором.

-<future<tuple<decat_t<FutTypes>...>>>, если входы фиксированы в количестве.

Postconditions:

Все входные фьючерсы действительны() == ложны.

Все входные данные shared_futures valid() == true.

- действительный() == истинный.

template <typename T>
  future<V> make_ready_future(T&& value);  // EXTENSION
future<void> make_ready_future();  // EXTENSION
template <typename T>
  future<T> make_ready_future(exception_ptr ex);  // DEPRECATED
template <typename T, typename E>
  future<T> make_ready_future(E ex);  // DEPRECATED

Remark:

где<V>определено следующим образом: Пусть<U>будет<decay_t<T>>. Тогда<V><X&>, если<U>равен<reference_wrapper<X>>, в противном случае<V><U>.

Effects:

- прототип ценности: Значение, которое передается в функцию, перемещается в общее состояние возвращенного будущего, если оно является значением r. В противном случае ценность копируется в общее состояние возвращенного будущего.

- исключение: Исключение, которое переходит в функцию, копируется в общее состояние возвращаемого будущего.

.

Returns:

- a ready future with the value set with value

- a ready future with the exception set with ex

- готовое будущеес набором значений (void).

Postcondition:

Возвращенное будущее, действительное() == истинный

Возвращенное будущее, is_ready() = true

Возвращенное будущее, имеет значение () = истинное или имеет исключение () в зависимости от прототипа.

exceptional_ptr make_exceptional_future(exception_ptr ex);  // EXTENSION
template <typename E>
  exceptional_ptr make_exceptional_future(E ex);  // EXTENSION
exceptional_ptr make_exceptional_future();  // EXTENSION

Effects:

Исключение, которое передается в функцию, или текущее исключение, если не указан параметр, перемещается в возвращенное значение<exceptional_ptr>. В противном случае исключение копируется в возвращенный<exceptional_ptr>.

Returns:

исключительный экземпляр _ptr, неявно конвертируемый в будущее

template <typename T>
  future<typename decay<T>::type> make_future(T&& value);  // DEPRECATED
future<void> make_future();  // DEPRECATED

Effects:

Значение, которое передается в функцию, перемещается в общее состояние возвращаемой функции, если оно является значением r. В противном случае значение копируется в общее состояние возвращаемой функции. .

Returns:

- Future, если функция имеет значение типа T

- Future, если функция не вводится.

Postcondition:

- Returned future, valid() == истинный

Возвращенное будущее, is_ready() = true

See:

make_ready_future()

template <typename T>
  shared_future<typename decay<T>::type> make_shared_future(T&& value);  // DEPRECATED
shared_future<void> make_shared_future();  // DEPRECATED

Effects:

Значение, которое передается в функцию, перемещается в общее состояние возвращаемой функции, если оно является значением r. В противном случае значение копируется в общее состояние возвращаемой функции. .

Returns:

- shared_future, если функция имеет значение типа T

- shared_future, если функция не вводится.

Postcondition:

- Returned shared_future, valid() == истинный

- Returned shared_future, is_ready() = true

See:

make_ready_future() and future<>::share()


PrevUpHomeNext

Статья Synchronization раздела 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:54:22/0.12033987045288/1