C++ 中处理互斥量 是一个优秀的教程。 您只需要用 boost 替换 std 和 ting。
互斥量、锁、条件变量原理 增加了对互斥量、锁和条件变量设计决策的原理说明。
除了 C++11 标准锁之外,Boost.Thread 还提供了其他锁和一些实用程序,可帮助用户使他们的代码线程安全。
![]() |
注意 |
---|---|
本教程改编自 BETA 编程语言中面向对象编程的并发章节以及 Andrei Alexandrescu 的论文“多线程和 C++ 类型系统”,并应用于 Boost 库。 |
例如,考虑对银行账户类进行建模,该类支持从多个位置同时存款和取款(可以说是多线程编程的“Hello, World”)。
这里,组件是 Callable
概念的模型。
在 C++11 (Boost) 中,组件的并发执行是通过 std::thread
(boost::thread
) 实现的
boost::thread thread1(S);
其中 S
是 Callable
的模型。 这个表达式的意思是 S()
的执行将与执行该表达式的当前执行线程同时进行。
以下示例包括一个人的银行帐户 (Joe) 和两个组件,一个对应于银行代理向 Joe 的帐户存款,另一个代表 Joe。 Joe 只会从帐户中取款
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; }
银行代理会不时地将 500 美元存入 JoesAccount
。Joe
也会类似地从他的帐户中取出 100 美元。 这些句子描述了 bankAgent
和 Joe
是同时执行的。
只要组件 bankAgent
和 Joe
不在同一时间访问 JoesAccount
,上面的例子就可以很好地工作。 然而,不能保证这种情况不会发生。 我们可以使用互斥量来保证对每个银行的独占访问。
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 习惯用法,我们可以使用作用域锁简化很多,在下面的代码中,guard 的构造函数锁定传入的对象 mtx_
,而 guard 的析构函数解锁 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 类来实现 ATM 取款交易。 要求很简单。 ATM 交易包括两次提款 - 一次用于实际现金,一次用于 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 在已被 guard 锁定后,又被 Withdraw 锁定。 运行此类代码时,会发生以下两种情况之一。
由于 boost::mutex
不是递归的,我们需要使用其递归版本 boost::recursive_mutex
。
class BankAccount : public basic_lockable_adapter<recursive_mutex> { // ... };
调用者确保的锁定方法更加灵活且效率最高,但非常危险。 在使用调用者确保的锁定的实现中,BankAccount 仍然拥有一个互斥量,但其成员函数根本不操作它。 Deposit 和 Withdraw 不再是线程安全的。 相反,客户端代码负责正确锁定 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 对象。 在设计应用程序时,区分对有界代码和无界代码施加的要求非常重要。 如果您的类对无界代码提出了不适当的要求,这通常表明封装已失效。
总而言之,如果在设计多线程类时采用内部锁定,则会使自己面临效率低下或死锁的风险。 另一方面,如果您依赖于调用者提供的锁定,则会使您的类容易出错且难以使用。 最后,外部锁定通过将其全部留给客户端代码来完全避免了该问题。
![]() |
注意 |
---|---|
本教程改编自 Andrei Alexandrescu 的论文“多线程和 C++ 类型系统”,并应用于 Boost 库。 |
那么该怎么办呢? 理想情况下,BankAccount 类应执行以下操作
让我们做一个有价值的观察:每当您锁定 BankAccount 时,您都可以通过使用 lock_guard<BankAccount>
对象来做到这一点。 将此语句翻转过来,只要有 lock_guard<BankAccount>
,某处也会有一个锁定的 BankAccount
。 因此,您可以将 lock_guard<BankAccount>
对象视为 - 并使用它 - 作为许可证。 拥有 lock_guard<BankAccount>
使您有权执行某些操作。lock_guard<BankAccount>
对象不应被复制或别名(它不是可传递的许可证)。
BankAccount
对象就会保持锁定状态。lock_guard<BankAccount>
被销毁时,BankAccount
的互斥锁将被释放。最终的效果是在你的代码中的任何时刻,如果拥有一个 lock_guard<BankAccount>
对象,就能保证一个 BankAccount
被锁定。(但是,你并不确切知道哪个 BankAccount
被锁定,这个问题我们稍后会解决。)
现在,让我们对 Boost.Thread 中定义的 lock_guard
类模板进行一些增强。我们将增强后的版本称为 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
传递给函数,也不能让函数返回它们。extern strict_lock<BankAccount> Foo(); // compile-time error extern void Bar(strict_lock<BankAccount>); // compile-time error
strict_lock
传递给函数或从函数返回。// 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>
就意味着
现在我们有了如此严格的 strict_lock
,我们如何利用它的力量来为 BankAccount 定义一个安全、灵活的接口呢?想法如下:
strict_lock<BankAccount>
类型的参数。第一个版本是内部锁定的;第二个版本需要外部锁定。通过要求客户端代码创建一个 strict_lock<BankAccount>
对象,可以在编译时强制执行外部锁定。一句(被篡改的)谚语说:一小段代码胜过千言万语,所以这里是新的 BankAccount 类:
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
作为一个模板提供了额外的安全性。在这种设计中,BankAccount 将从 Lockable 接口派生。strict_lock
将操作 Lockable 引用,因此不需要模板。这种方法是合理的;但是,它提供的编译时保证较少。拥有一个 strict_lock
对象只能说明某个从 Lockable 派生的对象当前被锁定。在模板方法中,拥有一个 strict_lock<BankAccount>
提供了更强的保证——它是一个 BankAccount 保持锁定状态。
这里有一个模棱两可的词——我提到 ATMWithdrawal 相当安全。它实际上并不安全,因为没有强制执行 strict_lock<BankAccount>
对象锁定相应的 BankAccount 对象。类型系统仅确保某些 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>&)
期间锁定某个 BankAccount 对象。我们必须在运行时强制执行究竟哪个对象被锁定。
如果我们的方案仍然需要运行时检查,那么它有什么用呢?一个粗心或恶意的程序员可以很容易地锁定错误的对象,并在没有实际锁定的情况下操纵任何 BankAccount。
首先,让我们解决恶意问题。C 语言需要程序员付出大量的注意力和纪律。C++ 在这方面取得了一些进展,要求略低,但仍然从根本上信任程序员。这些语言不关心恶意(例如 Java 关心)。毕竟,你可以通过“适当”地使用强制转换来破坏任何 C/C++ 设计(如果适当在这种情况下是一个合适的词)。
该方案很有用,因为程序员忘记任何锁定的可能性远大于程序员记得锁定,但锁定了错误对象的可能性。
使用 strict_lock
允许对最常见的错误来源进行编译时检查,并对不太常见的问题进行运行时检查。
让我们看看如何强制执行相应的 BankAccount 对象被锁定。首先,我们需要向 strict_lock
类模板添加一个成员函数。bool strict_lock<T>::owns_lock(Lockable*)
函数返回对锁定对象的引用。
template <class Lockable> class strict_lock { ... as before ... public: bool owns_lock(Lockable* mtx) const { return mtx==&obj_; } };
其次,BankAccount 需要使用此函数将锁定的对象与 this 进行比较。
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 类,它持有和操作一个 BankAccount 对象。
class AccountManager : public basic_lockable_adapter<boost::mutex> { BankAccount checkingAcct_; BankAccount savingsAcct_; ... };
让我们还假设,根据设计,AccountManager 在访问其 BankAccount 成员时必须保持锁定状态。问题是,我们如何使用 C++ 类型系统来表达这种设计约束?我们如何声明“只有在锁定其父 AccountManager 对象后,你才能访问此 BankAccount 对象”?
解决方案是使用一个小桥模板 externally_locked
来控制对 BankAccount 的访问。
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 类型的对象,并且实际上通过 get 和 set 成员函数提供对该对象的完全访问权限,前提是你传递一个对 strict_lock<Owner>
对象的引用。
AccountManager 没有将 checkingAcct_
和 savingsAcct_
设置为 BankAccount
类型,而是持有 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_
封装的 BankAccount 对象,你需要调用 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_
的声明向代码阅读者清楚地表明该变量受到 AccountManager 上的锁保护。其次,该设计使得在没有实际锁定 BankAccount 的情况下无法操纵这两个帐户。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(); }
我们需要一种方法,在处理 savingsAcct_
期间,将所有权从 unique_lock
转移到 strict_lock
,然后在 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(); }
为了使这段代码可编译,我们需要存储 Lockable 或 unique_lock<Lockable>
引用,具体取决于构造函数。我们还需要存储我们存储的是哪种引用,并在析构函数中调用 Lockable 的 unlock
或恢复所有权。
这对我来说太复杂了。另一种可能性是定义一个嵌套的严格锁类。缺点是,我们不是只有一个严格锁,而是有两个,我们需要复制每个接受 strict_lock
的函数,或者将这些函数定义为模板。模板函数的问题是我们不再能从 C++ 类型系统中获益。我们必须添加一些静态元函数来检查 Locker 参数是否是严格锁。问题是,我们真的无法检查这一点,或者可以吗?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
需要对互斥锁的引用,否则将抛出异常。
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
。我们可以在调试模式下添加测试,以确保 Lockable 对象已被锁定。
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));
使用 boost::bind
int result = boost::with_lock_guard( m, boost::bind(func, 2, boost::ref(a)) );
或使用 lambda 表达式
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 );
互斥锁对象有助于防止数据竞争,并允许线程之间的数据进行线程安全同步。线程通过调用锁函数之一获得互斥锁对象的所有权,并通过调用相应的解锁函数放弃所有权。互斥锁可以是递归的或非递归的,并且可以同时授予一个或多个线程的所有权。 Boost.Thread 提供具有独占所有权语义的递归和非递归互斥锁,以及共享所有权(多读者/单写者)互斥锁。
Boost.Thread 支持锁对象四个基本概念:Lockable
、TimedLockable
、SharedLockable
和 UpgradeLockable
。每种互斥锁类型都实现这些概念中的一个或多个,各种锁类型也是如此。
// #include <boost/thread/lockable_concepts.hpp> namespace boost { template<typename L> class BasicLockable; // EXTENSION }
BasicLockable
概念对独占所有权进行建模。如果以下表达式格式良好并具有指定的语义(m
表示 L
类型的值),则类型 L
满足 BasicLockable
要求
通过调用 lock()
获取的锁所有权必须通过调用 unlock()
释放。
如果互斥锁不是递归的,则调用线程不拥有互斥锁。
当前线程阻塞,直到可以为当前线程获得所有权。
对同一对象执行先前的 unlock()
操作与此操作同步。
当前线程拥有 m
。
void
.
如果发生错误,则抛出 lock_error
。
operation_not_permitted:如果线程没有执行该操作的权限。
resource_deadlock_would_occur:如果实现检测到将发生死锁。
device_or_resource_busy:如果互斥锁已锁定,并且无法阻塞。
如果抛出异常,则不应为当前线程获取锁。
当前线程拥有 m
。
此操作与后续获得同一对象所有权的锁操作同步。
释放当前线程对 m
的锁定。
void
.
无。
// #include <boost/thread/lockable_traits.hpp> namespace boost { namespace sync { template<typename L> class is_basic_lockable;// EXTENSION } }
互斥锁上的一些算法通过 SFINAE 使用此特性。
如果参数 L 满足 Lockable
要求,则此特性为 true_type。
![]() |
警告 |
---|---|
如果定义了 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
的值)。
m.try_lock
()
通过调用 try_lock()
获取的锁所有权必须通过调用 unlock()
释放。
如果互斥锁不是递归的,则调用线程不拥有互斥锁。
尝试为当前线程获取所有权而不阻塞。
如果 try_lock()
返回 true,则对同一对象先前的 unlock()
操作与此操作同步。
由于 lock()
不与随后的失败的 try_lock()
同步,因此可见性规则非常弱,以至于在失败之后几乎无法了解状态,即使在没有虚假失败的情况下也是如此。
bool
.
如果为当前线程获取了所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
。
无。
// #include <boost/thread/lockable_traits.hpp> namespace boost { namespace sync { template<typename L> class is_lockable;// EXTENSION } }
互斥锁上的一些算法通过 SFINAE 使用此特性。
如果参数 L 满足 Lockable
要求,则此特性为 true_type。
![]() |
警告 |
---|---|
如果定义了 BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES,则需要为你可能构建的 Lockable 模型专门化此 trait。 |
用户可能要求传递给算法的互斥锁是递归的。 无法使用模板元编程来检查锁是否是递归的。 这是以下 trait 的动机。
// #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 } }
默认情况下,trait 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 } }
如果 is_basic_lockable 且 is_recursive_mutex_sur_parole,则此 trait 为 true_type。
// #include <boost/thread/lockable_traits.hpp> namespace boost { namespace sync { template<typename L> class is_recursive_lockable;// EXTENSION } }
如果 is_lockable 且 is_recursive_mutex_sur_parole,则此 trait 为 true_type。
// #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
实例化的值表达式
m.try_lock_for
(rel_time)
m.try_lock_until
(abs_time)
通过调用 try_lock_for
或 try_lock_until
获取的锁所有权必须通过调用 unlock
释放。
如果互斥锁不是递归的,则调用线程不拥有互斥锁。
尝试为当前线程获取所有权。 阻塞,直到可以获取所有权或达到指定时间。 如果指定时间已经过去,则行为与 try_lock()
相同。
如果 try_lock_until()
返回 true,则对同一对象先前的 unlock()
操作与此操作同步。
bool
.
如果为当前线程获取了所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
。
无。
如果互斥锁不是递归的,则调用线程不拥有互斥锁。
等效于
。try_lock_until
(chrono::steady_clock::now() + rel_time)
如果 try_lock_for()
返回 true,则对同一对象先前的 unlock()
操作与此操作同步。
![]() |
警告 |
---|---|
自 4.00 起已弃用。以下表达式是版本 2 所必需的,但现在已弃用。 |
变量
rel_time
表示与 boost::system_time
算术兼容的未指定 DurationType
的实例的值,并且abs_time
表示 boost::system_time
实例的值表达式
m.timed_lock
(rel_time)
m.timed_lock
(abs_time)
通过调用 timed_lock()
获取的锁所有权必须通过调用 unlock()
释放。
尝试为当前线程获取所有权。 阻塞,直到可以获取所有权或达到指定时间。 如果指定时间已经过去,则行为与 try_lock()
相同。
如果为当前线程获取了所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
。
如果发生错误,则抛出 lock_error
。
// #include <boost/thread/lockable_concepts.hpp> namespace boost { template<typename L> class SharedLockable; // C++14 }
SharedLockable
concept 是 TimedLockable
concept 的改进,允许共享所有权和独占所有权。 这是标准的多读者/单写者模型:最多只能有一个线程拥有独占所有权,如果任何线程拥有独占所有权,则其他线程不能拥有共享或独占所有权。 或者,许多线程可以拥有共享所有权。
如果类型 L
满足 SharedLockable
的要求,则它必须满足 TimedLockable
的要求,并且以下表达式格式良好且具有指定的语义。
变量
m
表示类型 L
的值,rel_time
表示 chrono::duration
实例化的值,并且abs_time
表示 chrono::time_point
实例化的值表达式
m.lock_shared()
();
m.try_lock_shared
()
m.try_lock_shared_for
(rel_time)
m.try_lock_shared_until
(abs_time)
m.unlock_shared()
();
通过调用 lock_shared()
, try_lock_shared()
, try_lock_shared_for
或 try_lock_shared_until
获取的锁的所有权必须通过调用 unlock_shared()
释放。
当前线程阻塞,直到可以为当前线程获得共享所有权。
当前线程拥有 m
的共享所有权。
如果发生错误,则抛出 lock_error
。
尝试在不阻塞的情况下为当前线程获取共享所有权。
如果为当前线程获得了共享所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的共享所有权。
如果发生错误,则抛出 lock_error
。
尝试为当前线程获取共享所有权。 阻塞直到获得共享所有权,或者经过指定的时间。 如果指定的时间已经过去,则表现为 try_lock_shared()
。
如果为当前线程获得了共享所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的共享所有权。
如果发生错误,则抛出 lock_error
。
尝试为当前线程获取共享所有权。 阻塞直到获得共享所有权,或者达到指定的时间。 如果指定的时间已经过去,则表现为 try_lock_shared()
。
如果为当前线程获得了共享所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的共享所有权。
如果发生错误,则抛出 lock_error
。
当前线程拥有 m
的共享所有权。
释放当前线程对 m
的共享所有权。
当前线程不再拥有 m
的共享所有权。
无
![]() |
警告 |
---|---|
自 3.00 版本起已弃用。 以下表达式在版本 2 中是必需的,但现在已弃用。 |
变量
abs_time
表示 boost::system_time
实例的值表达式
m.timed_lock_shared(abs_time);
通过调用 timed_lock_shared()
获取的锁的所有权必须通过调用 unlock_shared()
释放。
尝试为当前线程获取共享所有权。 阻塞直到获得共享所有权,或者达到指定的时间。 如果指定的时间已经过去,则表现为 try_lock_shared()
。
如果为当前线程获得了共享所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的共享所有权。
如果发生错误,则抛出 lock_error
。
m.lock_upgrade()
m.unlock_upgrade()
m.try_lock_upgrade()
m.try_lock_upgrade_for(rel_time)
m.try_lock_upgrade_until(abs_time)
m.try_unlock_shared_and_lock()
m.try_unlock_shared_and_lock_for(rel_time)
m.try_unlock_shared_and_lock_until(abs_time)
m.unlock_and_lock_shared()
m.try_unlock_shared_and_lock_upgrade()
m.try_unlock_shared_and_lock_upgrade_for(rel_time)
m.try_unlock_shared_and_lock_upgrade_until(abs_time)
m.unlock_and_lock_upgrade()
m.unlock_upgrade_and_lock()
m.try_unlock_upgrade_and_lock()
m.try_unlock_upgrade_and_lock_for(rel_time)
m.try_unlock_upgrade_and_lock_until(abs_time)
m.unlock_upgrade_and_lock_shared()
// #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
实例化的值表达式
m.lock_upgrade
();
m.unlock_upgrade
()
m.try_lock_upgrade
()
m.try_lock_upgrade_for
(rel_time)
m.try_lock_upgrade_until
(abs_time)
m.unlock_and_lock_shared
()
m.unlock_and_lock_upgrade
();
m.unlock_upgrade_and_lock
();
m.try_unlock_upgrade_and_lock
()
m.try_unlock_upgrade_and_lock_for
(rel_time)
m.try_unlock_upgrade_and_lock_until
(abs_time)
m.unlock_upgrade_and_lock_shared
();
如果定义了 `BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS,则还需要以下表达式
m.try_unlock_shared_and_lock
();
m.try_unlock_shared_and_lock_for
(rel_time);
m.try_unlock_shared_and_lock_until
(abs_time);
m.try_unlock_shared_and_lock_upgrade
();
m.try_unlock_shared_and_lock_upgrade_for
(rel_time);
m.try_unlock_shared_and_lock_upgrade_until
(abs_time);
通过调用 lock_upgrade()
获取的锁所有权必须通过调用 unlock_upgrade()
来释放。 如果通过调用 unlock_xxx_and_lock_yyy()
函数之一更改了所有权类型,则必须通过调用与新的所有权级别对应的解锁函数来释放所有权。
调用线程不拥有互斥锁。
当前线程阻塞,直到可以为当前线程获得升级所有权。
当前线程拥有 m
的升级所有权。
对同一对象之前的
操作与此操作同步。unlock_upgrade
()
如果发生错误,则抛出 lock_error
。
当前线程拥有 m
的升级所有权。
释放当前线程对 m
的升级所有权。
当前线程不再拥有 m
的升级所有权。
此操作与后续获得同一对象所有权的锁操作同步。
无
调用线程不拥有互斥锁。
尝试为调用线程获取互斥锁的升级所有权,而不会阻塞。 如果未获得升级所有权,则不会产生任何影响,并且 try_lock_upgrade() 立即返回。
如果为当前线程获取了升级所有权,则为 true
,否则为 false
。
如果调用返回 true
,则当前线程拥有 m
的升级所有权。
如果
返回 true,则对同一对象之前的 try_lock_upgrade
()
操作与此操作同步。unlock_upgrade
()
无
调用线程不拥有互斥锁。
如果 rel_time
的滴答周期不能完全转换为本机滴答周期,则该持续时间应向上舍入到最接近的本机滴答周期。 尝试在 rel_time
指定的相对超时时间内为调用线程获取升级锁所有权。 如果 rel_time
指定的时间小于或等于 rel_time.zero()
,则该函数尝试获取所有权而不阻塞(就像通过调用
一样)。 只有在获得互斥对象升级所有权的情况下,该函数才会在 try_lock_upgrade
()rel_time
指定的超时时间内返回。
如果为当前线程获取了升级所有权,则为 true
,否则为 false
。
如果调用返回 true
,则当前线程拥有 m
的升级所有权。
如果
返回 true,则在同一对象上先前的 try_lock_upgrade_for
(rel_time)
操作与此操作同步。unlock_upgrade
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程不拥有互斥锁。
该函数尝试获取互斥锁的升级所有权。如果 abs_time
已经过去,则该函数会尝试在不阻塞的情况下获取升级所有权(就像调用
一样)。只有在该函数已获得互斥锁对象的升级所有权时,该函数才会早于 try_lock_upgrade
()abs_time
指定的绝对超时时间返回。
如果为当前线程获取了升级所有权,则为 true
,否则为 false
。
如果调用返回 true
,则当前线程拥有 m
的升级所有权。
如果
返回 true,则在同一对象上先前的 try_lock_upgrade_until
(abs_time)
操作与此操作同步。unlock_upgrade
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程必须持有互斥锁上的共享锁。
该函数尝试以原子方式将调用线程的所有权从共享转换为独占,而不阻塞。要使此转换成功,此线程必须是唯一持有该锁任何所有权的线程。如果转换不成功,则保留 m 的共享所有权。
如果为当前线程获取了独占所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的独占所有权。
如果
返回 true,则在同一对象上先前的 try_unlock_shared_and_lock
()
和随后的锁操作与此操作同步。unlock
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程应持有互斥锁上的共享锁。
如果 rel_time
的时钟周期不能完全转换为本机时钟周期,则持续时间应向上舍入到最接近的本机时钟周期。该函数尝试在 rel_time
指定的相对超时时间内,以原子方式将调用线程的所有权从共享转换为独占。如果 rel_time
指定的时间小于或等于 rel_time.zero()
,则该函数会尝试在不阻塞的情况下获取独占所有权(就像调用 try_unlock_shared_and_lock()
一样)。只有在该函数已获得互斥锁对象的独占所有权时,该函数才会在 rel_time
指定的超时时间内返回。要使此转换成功,在转换时,此线程必须是唯一持有该锁任何所有权的线程。如果转换不成功,则保留互斥锁的共享所有权。
如果为当前线程获取了独占所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的独占所有权。
如果
返回 true,则在同一对象上先前的 try_unlock_shared_and_lock_for
(rel_time)
和随后的锁操作与此操作同步。unlock
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程应持有互斥锁上的共享锁。
该函数尝试在 abs_time
指定的绝对超时时间内,以原子方式将调用线程的所有权从共享转换为独占。如果 abs_time
已经过去,则该函数会尝试在不阻塞的情况下获取独占所有权(就像调用 try_unlock_shared_and_lock()
一样)。只有在该函数已获得互斥锁对象的独占所有权时,该函数才会在 abs_time
指定的绝对超时时间内返回。要使此转换成功,在转换时,此线程必须是唯一持有该锁任何所有权的线程。如果转换不成功,则保留互斥锁的共享所有权。
如果为当前线程获取了独占所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的独占所有权。
如果
返回 true,则在同一对象上先前的 try_unlock_shared_and_lock_until
(rel_time)
和随后的锁操作与此操作同步。unlock
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程应对 m
持有独占锁。
以原子方式将调用线程的所有权从独占转换为共享。
当前线程拥有 m
的共享所有权。
此操作与随后获取同一对象所有权的锁操作同步。
无
调用线程应持有互斥锁上的共享锁。
该函数尝试以原子方式将调用线程的所有权从共享转换为升级,而不阻塞。要使此转换成功,不得有任何线程持有此对象的升级所有权。如果转换不成功,则保留互斥锁的共享所有权。
如果为当前线程获取了升级所有权,则为 true
,否则为 false
。
如果调用返回 true
,则当前线程拥有 m
的升级所有权。
如果
返回 true,则在同一对象上先前的 try_unlock_shared_and_lock_upgrade
()
和随后的锁操作与此操作同步。unlock_upgrade
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程应持有互斥锁上的共享锁。
如果 rel_time
的时钟周期不能完全转换为本机时钟周期,则持续时间应向上舍入到最接近的本机时钟周期。该函数尝试在 rel_time
指定的相对超时时间内,以原子方式将调用线程的所有权从共享转换为升级。如果 rel_time
指定的时间小于或等于 rel_time.zero()
,则该函数会尝试在不阻塞的情况下获取升级所有权(就像调用
一样)。只有在该函数已获得互斥锁对象的独占所有权时,该函数才会在 try_unlock_shared_and_lock_upgrade
()rel_time
指定的超时时间内返回。要使此转换成功,在转换时,不得有任何线程持有此对象的升级所有权。如果转换不成功,则保留 m 的共享所有权。
如果为当前线程获取了升级所有权,则为 true
,否则为 false
。
如果调用返回 true
,则当前线程拥有 m
的升级所有权。
如果
返回 true,则在同一对象上先前的 try_unlock_shared_and_lock_upgrade_for
(rel_time)
和随后的锁操作与此操作同步。unlock_upgrade
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程应持有互斥锁上的共享锁。
该函数尝试原子性地将调用线程的锁所有权从共享转换为升级,转换的绝对超时时间由 abs_time
指定。如果 abs_time
已经过期,则该函数尝试获取升级锁所有权,且不阻塞(如同调用
一样)。只有在获取了互斥对象的升级锁所有权时,该函数才会在 try_unlock_shared_and_lock_upgrade
()abs_time
指定的绝对超时时间之前返回。为了使此次转换成功,在转换的时刻,必须没有任何线程持有该对象的升级锁所有权。如果转换不成功,则互斥对象的共享锁所有权会被保留。
如果为当前线程获取了升级所有权,则为 true
,否则为 false
。
如果调用返回 true
,则当前线程拥有 m
的升级所有权。
如果
返回 true,则先前的 try_unlock_shared_and_lock_upgrade_until
(rel_time)
以及随后的对同一对象进行加锁操作会与此操作同步。unlock_upgrade
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
当前线程拥有 m
的独占锁所有权。
原子性地释放当前线程拥有的 m
的独占锁所有权,并获取 m
的升级锁所有权,且不阻塞。
当前线程拥有 m
的升级所有权。
此操作与随后获取同一对象所有权的锁操作同步。
无
当前线程拥有 m
的升级所有权。
原子性地释放当前线程拥有的 m
的升级锁所有权,并获取 m
的独占锁所有权。如果有其他线程持有共享锁所有权,则会阻塞,直到可以获取独占锁所有权为止。
当前线程拥有 m
的独占锁所有权。
此操作与先前的
以及随后的获取同一对象锁所有权的加锁操作同步。unlock_shared()
()
无
调用线程应持有互斥对象的升级锁。
该函数尝试原子性地将调用线程的锁所有权从升级转换为独占,且不阻塞。为了使此次转换成功,此线程必须是唯一持有该锁的任何所有权的线程。如果转换不成功,则 `m` 的升级锁所有权会被保留。
如果为当前线程获取了独占所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的独占所有权。
如果
返回 true,则先前的 try_unlock_upgrade_and_lock
()
以及随后的对同一对象进行加锁操作会与此操作同步。unlock
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程应持有互斥对象的升级锁。
如果 rel_time
的时钟周期不能精确地转换为原生时钟周期,则持续时间应向上舍入到最近的原生时钟周期。该函数尝试原子性地将调用线程的锁所有权从升级转换为独占,转换的相对超时时间由 rel_time
指定。如果 rel_time
指定的时间小于或等于 rel_time.zero()
,则该函数尝试获取独占锁所有权,且不阻塞(如同调用
一样)。只有在获取了互斥对象的独占锁所有权时,该函数才会在 try_unlock_upgrade_and_lock
()rel_time
指定的超时时间内返回。为了使此次转换成功,在此次转换的时刻,此线程应是唯一持有该锁的任何所有权的线程。如果转换不成功,则 `m` 的升级锁所有权会被保留。
如果为当前线程获取了独占所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的独占所有权。
如果
返回 true,则先前的 try_unlock_upgrade_and_lock_for
(rel_time)
以及随后的对同一对象进行加锁操作会与此操作同步。unlock
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
调用线程应持有互斥对象的升级锁。
该函数尝试原子性地将调用线程的锁所有权从升级转换为独占,转换的绝对超时时间由 abs_time
指定。如果 abs_time
已经过期,则该函数尝试获取独占锁所有权,且不阻塞(如同调用
一样)。只有在获取了互斥对象的独占锁所有权时,该函数才会在 try_unlock_upgrade_and_lock
()abs_time
指定的绝对超时时间之前返回。为了使此次转换成功,在此次转换的时刻,此线程应是唯一持有该锁的任何所有权的线程。如果转换不成功,则 `m` 的升级锁所有权会被保留。
如果为当前线程获取了独占所有权,则返回 true
,否则返回 false
。
如果调用返回 true
,则当前线程拥有 m
的独占所有权。
如果
返回 true,则先前的 try_unlock_upgrade_and_lock_for
(rel_time)
以及随后的对同一对象进行加锁操作会与此操作同步。unlock
()
无
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
当前线程拥有 m
的升级所有权。
原子性地释放当前线程拥有的 m
的升级锁所有权,并获取 m
的共享锁所有权,且不阻塞。
当前线程拥有 m
的共享所有权。
此操作与先前的 unlock_shared()
以及随后的获取同一对象锁所有权的加锁操作同步。
无
// #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
概念实现的锁所有权。在销毁时,锁所有权被释放。这提供了 Lockable
对象的简单 RAII 风格的加锁和解锁,以方便异常安全的加锁和解锁。此外,lock_guard(Lockable & m,boost::adopt_lock_t)
构造函数 允许 boost::lock_guard
对象取得当前线程已经持有的锁的所有权。
当前线程拥有一个等价于调用 m.lock()
所获得的锁。
存储对 m
的引用。取得 m
的锁状态的所有权。
无。
在传递给构造函数的 Lockable
对象上调用 m.unlock()
。
无。
template <typename Lockable> lock_guard<Lockable> make_lock_guard(Lockable& m); // EXTENSION
一个 lock_guard,就像使用 {m}
初始化一样。
由调用 m.lock()
抛出的任何异常。
template <typename Lockable> lock_guard<Lockable> make_lock_guard(Lockable& m, adopt_lock_t); // EXTENSION
一个 lock_guard,就像使用 {m, adopt_lock}
初始化一样。
由调用 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)...));
m
必须处于解锁状态
在由 m
锁定的范围内调用 func
func(args...)
调用的结果
对 m.lock
和 func(args...)
的调用抛出的任何异常
m
处于解锁状态
如果没有 c++11 可变参数模板的支持,参数数量将限制为 4
如果没有右值引用支持,使用 boost::bind
调用类方法必须是 const
为了正确使用 lambda 宏,可能需要定义 BOOST_RESULT_OF_USE_DECLTYPE
// #include <boost/thread/lock_concepts.hpp> namespace boost { template<typename Lock> class StrictLock; }
StrictLock 是一种锁,它确保关联的互斥锁在锁的生命周期内被锁定。
如果以下表达式格式良好且具有指定的语义,则类型 L
满足 StrictLock 要求
L::mutex_type
is_strict_lock<L>
cl.owns_lock(m);
以及 BasicLockable<L::mutex_type>
其中
cl
表示类型为 L const&
的值,m
表示类型为 L::mutex_type const*
的值,类型 L::mutex_type 表示由此锁锁定的互斥锁。
由于语义“确保关联的互斥锁在锁的生命周期内被锁定。”不能通过语法要求来描述,因此 is_strict_lock_sur_parole
特征必须由定义锁的用户专门化,以便以下断言为真
is_strict_lock_sur_parole<L>::value == true
bool
严格锁是否正在锁定互斥锁 m
无。
以下类是 StrictLock
的模型
boost::lock_guard
:“口头保证”,因为用户可以使用 adopt_lock_t 构造函数重载而无需锁定互斥锁。// #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 }
unique_lock()
unique_lock(Lockable & m)
unique_lock(Lockable & m,boost::adopt_lock_t)
unique_lock(Lockable & m,boost::defer_lock_t)
unique_lock(Lockable & m,boost::try_to_lock_t)
unique_lock(shared_lock<mutex_type>&& sl, try_to_lock_t)
unique_lock(shared_lock<mutex_type>&&, const chrono::time_point<Clock, Duration>&)
unique_lock(shared_lock<mutex_type>&&, const chrono::duration<Rep, Period>&)
unique_lock(Lockable & m,boost::system_time const& abs_time)
template <class Clock, class Duration> unique_lock(Lockable & m,const chrono::time_point<Clock, Duration>& abs_time)
template <class Rep, class Period> unique_lock(Lockable & m,const chrono::duration<Rep, Period>& abs_time)
~unique_lock()
bool owns_lock() const
Lockable* mutex() const noexcept
explicit operator bool() const
Lockable* release()
// #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_CONVERSIONS 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()
成员函数,或者以非阻塞方式尝试获取锁,或者设置超时。因此,只有当锁对象已经锁定 Lockable
对象,或者以其他方式采用了 Lockable
对象的锁时,才会在析构函数中调用 unlock()
。
如果提供的 Lockable
类型本身符合 TimedLockable
概念(例如 boost::unique_lock<boost::timed_mutex>
),则 boost::unique_lock
的特化符合 TimedLockable
概念;如果提供的 Lockable
类型本身符合 Lockable
概念(例如 boost::unique_lock<boost::mutex>
),则符合 Lockable
概念;如果提供的 Lockable
类型本身符合 BasicLockable
概念,则符合 BasicLockable
概念。
如果 mutex()
返回指向 m
的指针,并且 owns_lock()
返回 true
,则称 boost::unique_lock
的实例 拥有 Lockable
m
的锁定状态。如果 拥有 Lockable
对象的锁定状态的对象被销毁,则析构函数将调用 mutex()->unlock()
。
boost::unique_lock
的成员函数不是线程安全的。特别是,boost::unique_lock
旨在模拟特定线程对 Lockable
对象的所有权,并且释放锁定状态所有权的成员函数(包括析构函数)必须由获取锁定状态所有权的同一线程调用。
创建一个没有关联互斥锁的锁对象。
owns_lock()
返回 false
。mutex()
返回 NULL
。
无。
存储对 m
的引用。调用 m.lock()
。
owns_lock()
返回 true
。mutex()
返回 &m
。
由调用 m.lock()
抛出的任何异常。
当前线程拥有 m
上的独占锁。
存储对 m
的引用。取得 m
的锁状态的所有权。
owns_lock()
返回 true
。mutex()
返回 &m
。
无。
存储对 m
的引用。
owns_lock()
返回 false
。mutex()
返回 &m
。
无。
存储对 m
的引用。调用 m.try_lock()
,如果调用返回 true
,则取得锁定状态的所有权。
mutex()
返回 &m
。如果对 try_lock()
的调用返回 true
,则 owns_lock()
返回 true
,否则 owns_lock()
返回 false
。
无。
提供的 Mutex
类型必须实现
。try_unlock_shared_and_lock
()
构造一个 boost::unique_lock
类型的对象。令 pm
为指向互斥量的指针,owns
为所有权状态。初始化 pm
为 nullptr,owns
为 false。如果 sl.
返回 owns_lock()
()false
,则设置 pm
为 sl.release()
的返回值。否则,如果 sl.
返回 owns_lock()
()true
,并且如果 sl.mutex()->try_unlock_shared_and_lock()
返回 true
,则设置 pm
为 sl.release()
返回的值,并设置 owns
为 true
。
如果 sl.owns_lock()
返回 true
并且 sl.mutex()->try_unlock_shared_and_lock()
返回 false
,则 sl
不会被修改。
无。
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
template <class Clock, class Duration> unique_lock(shared_lock<mutex_type>&& sl, const chrono::time_point<Clock, Duration>& abs_time);
提供的 Mutex
类型应实现
。try_unlock_shared_and_lock_until
(abs_time)
构造一个
类型的对象,初始化 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
。
如果 sl.owns_lock()
返回 true
并且 sl.mutex()->
返回 try_unlock_shared_and_lock_until
(abs_time)false
,则 sl
不会被修改。
无。
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
template <class Rep, class Period> unique_lock(shared_lock<mutex_type>&& sl, const chrono::duration<Rep, Period>& rel_time)
提供的 Mutex
类型应实现
。try_unlock_shared_and_lock_for
(rel_time)
构造一个
类型的对象,初始化 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
。
如果 sl.owns_lock()
返回 true
并且 sl.mutex()->
返回 try_unlock_shared_and_lock_for
(rel_time)false
,则 sl
不会被修改。
.
无。
仅当 Windows 平台上定义了 BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
和 BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
时可用
存储对 m
的引用。调用 m.timed_lock(abs_time)
,如果调用返回 true
,则获取锁状态的所有权。
mutex()
返回 &m
。 如果调用 timed_lock()
返回 true
,则 owns_lock()
返回 true
,否则 owns_lock()
返回 false
。
抛出调用 m.timed_lock(abs_time)
产生的任何异常。
存储对 m
的引用。 调用 m.
,如果调用返回 try_lock_until
(abs_time)true
,则获取锁状态的所有权。
mutex()
返回 &m
。 如果调用 try_lock_until
返回 true
,则 owns_lock()
返回 true
,否则 owns_lock()
返回 false
。
抛出调用 m.
产生的任何异常。try_lock_until
(abs_time)
存储对 m
的引用。 调用 m.
,如果调用返回 try_lock_for
(rel_time)true
,则获取锁状态的所有权。
mutex()
返回 &m
。 如果调用 try_lock_for
返回 true
,则 owns_lock()
返回 true
,否则 owns_lock()
返回 false
。
抛出调用 m.
产生的任何异常。try_lock_for
(rel_time)
如果 owns_lock()
返回 true
,则调用 mutex()
->
unlock()
。
无。
如果 *this
拥有与 *this
关联的 Lockable
对象的锁,则为 true
。
无。
指向与 *this
关联的 Lockable
对象的指针,如果没有此类对象,则为 NULL
。
无。
.
owns_lock()
()
无。
删除 *this
和 Lockable
对象之间的关联,而不影响 Lockable
对象的锁状态。 如果 owns_lock()
会返回 true
,则调用代码有责任确保 Lockable
被正确解锁。
指向调用时与 *this
关联的 Lockable
对象的指针,如果没有此类对象,则为 NULL
。
无。
*this
不再与任何 Lockable
对象关联。 mutex()
返回 NULL
并且 owns_lock()
返回 false
。
shared_lock()
shared_lock(Lockable & m)
shared_lock(Lockable & m,boost::adopt_lock_t)
shared_lock(Lockable & m,boost::defer_lock_t)
shared_lock(Lockable & m,boost::try_to_lock_t)
shared_lock(Lockable & m,boost::system_time const& abs_time)
~shared_lock()
bool owns_lock() const
Lockable* mutex() const
explicit operator bool() const
Lockable* release()
// #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
concept 进行建模,但不同于获取所提供的 Lockable
对象的独占所有权,锁定 boost::shared_lock
的实例会获取共享所有权。
类似于 boost::unique_lock
,它不仅提供 RAII 风格的锁定,还允许延迟获取锁,直到显式调用 lock()
成员函数,或者尝试以非阻塞方式或使用超时来获取锁。因此,只有当锁对象已锁定 Lockable
对象,或者以其他方式采用 Lockable
对象上的锁时,才会在析构函数中调用 unlock()
。
据说 boost::shared_lock
的实例拥有Lockable
m
的锁状态,如果 mutex()
返回指向 m
的指针,并且 owns_lock()
返回 true
。如果 拥有 Lockable
对象的锁状态的对象被销毁,则析构函数将调用 mutex()->unlock_shared()
。
boost::shared_lock
的成员函数不是线程安全的。特别是,boost::shared_lock
旨在对特定线程共享 Lockable
对象的所有权进行建模,并且释放锁状态所有权的成员函数(包括析构函数)必须由获取锁状态所有权的同一线程调用。
创建一个没有关联互斥锁的锁对象。
owns_lock()
返回 false
。mutex()
返回 NULL
。
无。
存储对 m
的引用。调用 m.lock_shared()
。
owns_lock()
返回 true
。mutex()
返回 &m
。
调用 m.lock_shared()
抛出的任何异常。
当前线程拥有 m
上的独占锁。
存储对 m
的引用。取得 m
的锁状态的所有权。
owns_lock()
返回 true
。mutex()
返回 &m
。
无。
存储对 m
的引用。
owns_lock()
返回 false
。mutex()
返回 &m
。
无。
存储对 m
的引用。调用 m.try_lock_shared()
,如果调用返回 true
,则获取锁状态的所有权。
mutex()
返回 &m
。如果对 try_lock_shared()
的调用返回 true
,则 owns_lock()
返回 true
,否则 owns_lock()
返回 false
。
无。
存储对 m
的引用。调用 m.timed_lock(abs_time)
,如果调用返回 true
,则获取锁状态的所有权。
mutex()
返回 &m
。如果对 timed_lock_shared()
的调用返回 true
,则 owns_lock()
返回 true
,否则 owns_lock()
返回 false
。
调用 m.timed_lock(abs_time)
抛出的任何异常。
如果 owns_lock()
返回 true
,则调用 mutex()
->
unlock_shared()
。
无。
如果 *this
拥有与 *this
关联的 Lockable
对象的锁,则为 true
。
无。
指向与 *this
关联的 Lockable
对象的指针,如果没有此类对象,则为 NULL
。
无。
无。
*this
与 Lockable
对象之间的关联将被移除,但不影响 Lockable
对象的锁状态。如果 owns_lock()
会返回 true
,则调用代码有责任确保 Lockable
被正确解锁。
指向调用时与 *this
关联的 Lockable
对象的指针,如果没有此类对象,则为 NULL
。
无。
*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_CONVERSIONS // 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
concept,但是它并没有获取提供的 Lockable
对象的独占所有权,而是获取升级所有权来锁定 boost::upgrade_lock
的实例。
类似于 boost::unique_lock
,它不仅提供 RAII 风格的锁定,还允许延迟获取锁,直到显式调用 lock()
成员函数,或者尝试以非阻塞方式或使用超时来获取锁。因此,只有当锁对象已锁定 Lockable
对象,或者以其他方式采用 Lockable
对象上的锁时,才会在析构函数中调用 unlock()
。
boost::upgrade_lock
的实例被称为拥有 Lockable
m
的锁状态,如果 mutex()
返回指向 m
的指针,并且 owns_lock()
返回 true
。如果一个拥有 Lockable
对象的锁状态的对象被销毁,那么析构函数将调用 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; };
为每个不同的 MutexType
提供成员 typedef scoped_try_lock
,作为对具有前面定义的类的 typedef。 每个构造函数和成员函数的语义与 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
概念的实现的拥有权。 在销毁时,拥有权将被释放。 这提供了简单的 RAII 风格的 BasicLockable
对象锁定,以方便异常安全的锁定和解锁。
boost::lock_guard
在传递给构造函数的 Lockable
对象上调用 m.unlock()
。
无。
// #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
的模型。
嵌套的严格锁是一个作用域锁保护器,通过获取嵌套锁的所有权,在作用域内确保互斥锁被锁定,如果尚未锁定,则在构造时锁定互斥锁,并在销毁时将所有权恢复到嵌套锁。
strict_lock
, boost::unique_lock
lk.mutex() != null_ptr
.
存储对锁参数 lk
的引用并获取其所有权。 如果锁不拥有互斥锁,则锁定它。
owns_lock(lk.mutex())
.
- 如果定义了 BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED 且 lk.mutex() == null_ptr,则返回 lock_error
- @c lk.lock() 可能抛出的任何异常。
将所有权恢复到嵌套锁。
此锁是否正在锁定该互斥锁。
template <typename Lockable> strict_lock<Lockable> make_strict_lock(Lockable& m); // EXTENSION
一个 strict_lock,就像用 {m}
初始化一样。
由调用 m.lock()
抛出的任何异常。
template <typename Lock> nested_strict_lock<Lock> make_nested_strict_lock(Lock& lk); // EXTENSION
一个 nested_strict_lock,就像用 {lk}
初始化一样。
调用 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);
const_strict_lock_ptr(T const& val, Lockable & m, adopt_lock_t tag);
存储对它的引用以及对值类型 val
的引用。
无。
// #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);
strict_lock_ptr(T const& val, Lockable & m, adopt_lock_t tag);
存储对它的引用以及对值类型 val
的引用。
无。
// #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
类型的对象,并且实际上通过 get 和 set 成员函数提供了对该对象的完全访问权限,前提是你传递了一个严格锁对象的引用。
这里仅描述了 respect to Lockable
的特殊性。
externally_locked(mutex_type& mtx, const T& obj);
T 是 CopyConstructible 的模型。
构造一个外部锁定的对象,复制被封装的类型。
任何由调用 T(obj)
抛出的异常。
externally_locked(mutex_type& mtx,T&& obj);
T 是 Movable 的模型。
通过移动被封装的类型,构造一个外部锁定的对象。
任何由调用 T(obj)
抛出的异常。
externally_locked(mutex_type& mtx);
T 是 DefaultConstructible 的模型。
通过默认构造被封装的类型,构造一个外部锁定的对象。
任何由调用 T()
抛出的异常。
externally_locked(externally_locked&& rhs);
T 是 Movable 的模型。
通过移动被封装的类型并复制互斥量引用来移动构造一个外部锁定的对象
任何由调用 T(T&&)
抛出的异常。
externally_locked(externally_locked& rhs);
T 是 Copyable 的模型。
通过复制被封装的类型并复制互斥量引用来复制构造一个外部锁定的对象
任何由调用 T(T&)
抛出的异常。
externally_locked& operator=(externally_locked&& rhs);
T 是 Movable 的模型。
通过移动被封装的类型并复制互斥量引用来移动赋值一个外部锁定的对象
任何由调用 T::operator=(T&&)
抛出的异常。
externally_locked& operator=(externally_locked const& rhs);
T 是 Copyable 的模型。
通过复制被封装的类型并复制互斥量引用来复制赋值一个外部锁定的对象
任何由调用 T::operator=(T&)
抛出的异常。
T& get(strict_lock<mutex_type>& lk); const T& get(strict_lock<mutex_type>& lk) const;
lk
参数必须锁定关联的互斥量。
被封装对象的引用
如果定义了 BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
并且运行时前提条件不满足,则抛出 lock_error
。
template <class Lock> T& get(nested_strict_lock<Lock>& lk); template <class Lock> const T& get(nested_strict_lock<Lock>& lk) const;
is_same<mutex_type, typename Lock::mutex_type>
并且 lk
参数必须锁定关联的互斥量。
被封装对象的引用
如果定义了 BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
并且运行时前提条件不满足,则抛出 lock_error
。
template <class Lock> T& get(Lock& lk); template <class Lock> T const& get(Lock& lk) const;
Lock
是 StrictLock
的模型,is_same<mutex_type, typename Lock::mutex_type>
并且 lk
参数必须锁定关联的互斥量。
被封装对象的引用
如果定义了 BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
并且运行时前提条件不满足,则抛出 lock_error
。
// #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
类型的对象,并且实际上通过 get 和 set 成员函数提供了对该对象的完全访问权限,前提是你传递了一个严格锁对象的引用。
这里仅描述了 respect to Lockable
的特殊性。
externally_locked<T&>(mutex_type& mtx, T& obj) noexcept;
构造一个外部锁定的对象,复制被封装的引用。
externally_locked(externally_locked&& rhs) noexcept;
通过移动被封装的类型并复制互斥量引用来移动一个外部锁定的对象
externally_locked& operator=(externally_locked&& rhs);
通过复制被封装的引用并复制互斥量引用来移动赋值一个外部锁定的对象
externally_locked& operator=(externally_locked const& rhs);
T 是 Copyable 的模型。
通过复制被封装的引用并复制互斥量引用来复制赋值一个外部锁定的对象
任何由调用 T::operator=(T&)
抛出的异常。
T& get(strict_lock<mutex_type>& lk); const T& get(strict_lock<mutex_type>& lk) const;
lk
参数必须锁定关联的互斥量。
被封装对象的引用
如果定义了 BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
并且运行时前提条件不满足,则抛出 lock_error
。
template <class Lock> T& get(nested_strict_lock<Lock>& lk); template <class Lock> const T& get(nested_strict_lock<Lock>& lk) const;
is_same<mutex_type, typename Lock::mutex_type>
并且 lk
参数必须锁定关联的互斥量。
被封装对象的引用
如果定义了 BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
并且运行时前提条件不满足,则抛出 lock_error
。
template <class Lock> T& get(Lock& lk); template <class Lock> T const& get(Lock& lk) const;
Lock
是 StrictLock
的模型,is_same<mutex_type, typename Lock::mutex_type>
并且 lk
参数必须锁定关联的互斥量。
被封装对象的引用
如果定义了 BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
并且运行时前提条件不满足,则抛出 lock_error
。
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
concept 实现的共享所有权。 在销毁时,所有权被释放。 这提供了 SharedLockable
对象的简单 RAII 风格的锁定,以方便异常安全共享锁定和解锁。 此外,
构造函数允许 shared_lock_guard
(SharedLockable &m, boost::adopt_lock_t)shared_lock_guard
对象获取当前线程已持有的锁的共享所有权。
存储对 m
的引用。 调用 m.
。lock_shared()
()
调用 m.
抛出的任何异常。lock_shared()
()
当前线程拥有 m
上的锁,该锁等同于通过调用 m.
获得的锁。lock_shared()
()
存储对 m
的引用。取得 m
的锁状态的所有权。
无。
在传递给构造函数的 SharedLockable
对象上调用 m.
。unlock_shared()
()
无。
// #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 风格,在构造时解锁锁,在销毁时锁定锁。 此外,它暂时转移所有权,以便无法使用 Lock 锁定互斥锁。
reverse_lock
的实例永远不 拥有 锁。
存储对 m
的引用。 如果 m
拥有其锁,则调用 m.
,然后通过调用 unlock
()m.release()
来存储互斥锁。
!m.
.
owns_lock()
() && m.mutex()==0
调用 m.
抛出的任何异常。unlock
()
// #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); }
template<typename ForwardIterator> void lock(ForwardIterator begin,ForwardIterator end);
ForwardIterator
的 value_type
必须实现 Lockable
concept(概念)。
以一种避免死锁的方式,按未指定且不确定的顺序锁定所提供范围内所有 Lockable
对象。 在任何顺序下,从多个线程并发调用此函数对于任何 mutex(或其它 lockable 对象)集合都是安全的,而没有死锁的风险。 如果对所提供范围内 Lockable
对象的任何 lock()
或 try_lock()
操作抛出异常,则该函数在退出前将释放该函数获取的任何锁。
所提供范围内所有 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);
对作为参数提供的每个 Lockable
对象调用 try_lock()
。 如果对 try_lock()
的任何调用返回 false
,则释放所有已获取的锁,并返回失败锁的从零开始的索引。
如果在提供的 Lockable
对象上执行的任何 try_lock()
操作抛出异常,则该函数在退出前将释放该函数获取的任何锁。
如果所有提供的 Lockable
对象现在都由调用线程锁定,则返回 -1
;否则返回无法锁定的对象的从零开始的索引。
调用提供的 Lockable
对象上的 try_lock()
抛出的任何异常。
如果函数返回 -1
,则所有提供的 Lockable
对象都由调用线程锁定。 否则,此函数获取的任何锁都将被释放。
template<typename ForwardIterator> ForwardIterator try_lock(ForwardIterator begin,ForwardIterator end);
ForwardIterator
的 value_type
必须实现 Lockable
concept(概念)。
对所提供范围内每个 Lockable
对象调用 try_lock()
。 如果对 try_lock()
的任何调用返回 false
,则释放所有已获取的锁,并返回引用失败锁的迭代器。
如果在提供的 Lockable
对象上执行的任何 try_lock()
操作抛出异常,则该函数在退出前将释放该函数获取的任何锁。
如果所有提供的 Lockable
对象现在都由调用线程锁定,则返回 end
;否则返回引用无法锁定的对象的迭代器。
调用提供的 Lockable
对象上的 try_lock()
抛出的任何异常。
如果函数返回 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
一个 boost::unique_lock
,就像用 unique_lock<Lockable>(mtx)
初始化一样。
调用
抛出的任何异常。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
一个 boost::unique_lock
,就像用 unique_lock<Lockable>(mtx, tag)
初始化一样。
调用
抛出的任何异常。boost::unique_lock
<Lockable>(mtx, tag)
template <typename ...Lockable> std::tuple<unique_lock<Lockable> ...> make_unique_locks(Lockable& ...mtx); // EXTENSION
锁定所有互斥量。
一个 std::tuple,其中包含拥有每个互斥量的唯一 boost::unique_lock
。
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
concept(概念) 以提供独占所有权互斥量。 在任何时间,至多一个线程可以拥有给定 boost::mutex
实例上的锁。 允许多个并发调用 lock()
,try_lock()
和 unlock()
。
typedef platform-specific-type native_handle_type; native_handle_type native_handle();
返回 native_handle_type
的实例,该实例可与特定于平台的 API 一起使用以操作底层实现。 如果不存在这样的实例,则 native_handle()
和 native_handle_type
不存在。
无。
#include <boost/thread/mutex.hpp> typedef mutex try_mutex;
boost::try_mutex
是 boost::mutex
的 typedef
,用于向后兼容以前版本的 boost。
#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
概念,以提供独占所有权的互斥锁。 在任何给定时间,最多只能有一个线程拥有 boost::timed_mutex
的实例的锁。 允许多个并发调用 lock()
、try_lock()
、timed_lock()
、timed_lock()
和 unlock()
。
typedef platform-specific-type native_handle_type; native_handle_type native_handle();
返回 native_handle_type
的实例,该实例可与特定于平台的 API 一起使用以操作底层实现。 如果不存在这样的实例,则 native_handle()
和 native_handle_type
不存在。
无。
#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()
以获取互斥锁的附加所有权级别。 在可以被另一个线程获取所有权之前,必须为单个线程获取的每个所有权级别调用一次 unlock()
。
typedef platform-specific-type native_handle_type; native_handle_type native_handle();
返回 native_handle_type
的实例,该实例可与特定于平台的 API 一起使用以操作底层实现。 如果不存在这样的实例,则 native_handle()
和 native_handle_type
不存在。
无。
#include <boost/thread/recursive_mutex.hpp> typedef recursive_mutex recursive_try_mutex;
boost::recursive_try_mutex
是 boost::recursive_mutex
的 typedef
,用于向后兼容 boost 的早期版本。
#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()
以获取互斥锁的附加所有权级别。 在可以被另一个线程获取所有权之前,必须为单个线程获取的每个所有权级别调用一次 unlock()
。
typedef platform-specific-type native_handle_type; native_handle_type native_handle();
返回 native_handle_type
的实例,该实例可与特定于平台的 API 一起使用以操作底层实现。 如果不存在这样的实例,则 native_handle()
和 native_handle_type
不存在。
无。
#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
提供了多读者/单写者互斥锁的实现。 它实现了 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 中缺少读者-写者优先级策略。 这是由于 Alexander Terekhov 提出的算法,该算法让操作系统决定哪个线程是下一个获得锁的线程,而无需关心是正在寻求独占锁还是共享锁。 这导致完全缺乏读者或写者饥饿。 只是公平。
#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
提供了多读者/单写者互斥锁的实现。 它实现了 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
提供了一个多读者/单写者互斥锁的空操作实现。 它是 UpgradeLockable
概念的模型。
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
提供了一种机制,允许一个线程等待另一个线程通知某个特定条件已变为真。 一般用法是,一个线程锁定一个互斥锁,然后在 condition_variable
或 condition_variable_any
的实例上调用 wait
。 当线程从等待中唤醒时,它会检查相应的条件是否现在为真,如果是,则继续。 如果条件不为真,则线程再次调用 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
返回之前再次被锁定。 这允许其他线程获取互斥锁以更新共享数据,并确保与条件相关联的数据被正确同步。
同时,另一个线程将条件设置为 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(); }
请注意,在更新共享数据之前,会锁定同一个互斥锁,但互斥锁不必在调用 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
具有更复杂的实现。
condition_variable()
~condition_variable()
void notify_one()
void notify_all()
void wait(boost::unique_lock<boost::mutex>& lock)
template<typename predicate_type> void wait(boost::unique_lock<boost::mutex>& lock, predicate_type pred)
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 pred)
template <class Clock, class Duration> cv_status wait_until(boost::unique_lock<boost::mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time)
template <class Rep, class Period> cv_status wait_for(boost::unique_lock<boost::mutex>& lock, const chrono::duration<Rep, Period>& rel_time)
template <class Clock, class Duration, class Predicate> bool wait_until(boost::unique_lock<boost::mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred)
template <class Rep, class Period, class Predicate> bool wait_for(boost::unique_lock<boost::mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred)
//#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 }; }
构造一个 condition_variable
类的对象。
如果发生错误,则抛出 boost::thread_resource_error
异常。
通过调用 notify_one
或 notify_all
(即使对 wait
或 timed_wait
的相应调用尚未返回) 已经通知了所有正在 *this
上等待的线程。
销毁对象。
无。
如果有任何线程当前在调用 wait
或 timed_wait
时被 阻塞 在 *this
上,则解除阻塞其中一个线程。
无。
如果有任何线程当前在调用 wait
或 timed_wait
时被 阻塞 在 *this
上,则解除阻塞所有这些线程。
无。
lock
必须由当前线程锁定,并且或者当前没有其他线程正在等待 *this
,或者在当前等待 *this
的所有线程中,对 wait
或 timed_wait
的调用中提供的 lock
对象执行 mutex()
成员函数将返回与此 wait
调用中 lock->mutex()
相同的值。
原子性地调用 lock.unlock()
并阻塞当前线程。 当调用 this->notify_one()
或 this->notify_all()
通知时,或虚假唤醒时,线程将被解除阻塞。 当线程被解除阻塞(无论什么原因),在 wait
调用返回之前,通过调用 lock.lock()
重新获取锁。 如果函数因异常退出,也会通过调用 lock.lock()
重新获取锁。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
如同
while(!pred()) { wait(lock); }
lock
必须由当前线程锁定,并且或者当前没有其他线程正在等待 *this
,或者在当前等待 *this
的所有线程中,对 wait
或 timed_wait
的调用中提供的 lock
对象执行 mutex()
成员函数将返回与此 wait
调用中 lock->mutex()
相同的值。
原子性地调用 lock.unlock()
并阻塞当前线程。 当调用 this->notify_one()
或 this->notify_all()
通知时,或者当 boost::get_system_time()
报告的时间等于或晚于指定的 abs_time
时,或虚假唤醒时,线程将被解除阻塞。 当线程被解除阻塞(无论什么原因),在 wait
调用返回之前,通过调用 lock.lock()
重新获取锁。 如果函数因异常退出,也会通过调用 lock.lock()
重新获取锁。
如果调用因为到达 abs_time
指定的时间而返回,则返回 false
,否则返回 true
。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
lock
必须由当前线程锁定,并且或者当前没有其他线程正在等待 *this
,或者在当前等待 *this
的所有线程中,对 wait
或 timed_wait
的调用中提供的 lock
对象执行 mutex()
成员函数将返回与此 wait
调用中 lock->mutex()
相同的值。
原子性地调用 lock.unlock()
并阻塞当前线程。 当调用 this->notify_one()
或 this->notify_all()
通知时,在 rel_time
参数指示的时间段过去之后,或虚假唤醒时,线程将被解除阻塞。 当线程被解除阻塞(无论什么原因),在 wait
调用返回之前,通过调用 lock.lock()
重新获取锁。 如果函数因异常退出,也会通过调用 lock.lock()
重新获取锁。
如果调用因为 rel_time
指定的时间段已经过去而返回,则返回 false
,否则返回 true
。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
![]() |
注意 |
---|---|
timed_wait 的 duration 重载很难正确使用。 在大多数情况下,应优先使用接受谓词的重载。 |
如同
while(!pred()) { if(!timed_wait(lock,abs_time)) { return pred(); } } return true;
lock
必须由当前线程锁定,并且或者当前没有其他线程正在等待 *this
,或者在当前等待 *this
的所有线程中,对 wait
或 wait_for
或 wait_until
的调用中提供的 lock
对象执行 mutex()
成员函数将返回与此 wait
调用中 lock->mutex()
相同的值。
原子地调用 lock.unlock()
并阻塞当前线程。当调用 this->notify_one()
或 this->notify_all()
通知时,或者当 Clock::now()
报告的时间等于或晚于指定的 abs_time
时,或者虚假唤醒时,线程将被解除阻塞。当线程被解除阻塞(无论何种原因)后,在 wait
返回前,将通过调用 lock.lock()
重新获取锁。如果函数抛出异常退出,也会通过调用 lock.lock()
重新获取锁。
如果因为到达 abs_time
指定的时间而返回,则返回 cv_status::timeout
,否则返回 cv_status::no_timeout
。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
lock
由当前线程锁定,并且要么没有其他线程当前在 *this
上等待,要么对当前正在 *this
上等待的所有线程中,调用 wait
或 wait_until
或 wait_for
时提供的 lock
对象的 mutex()
成员函数返回的值,与本次调用 wait
的 lock->mutex()
返回的值相同。
原子性地调用 lock.unlock()
并阻塞当前线程。 当调用 this->notify_one()
或 this->notify_all()
通知时,在 rel_time
参数指示的时间段过去之后,或虚假唤醒时,线程将被解除阻塞。 当线程被解除阻塞(无论什么原因),在 wait
调用返回之前,通过调用 lock.lock()
重新获取锁。 如果函数因异常退出,也会通过调用 lock.lock()
重新获取锁。
如果由于经过了 rel_time
指定的时间段而返回,则返回 cv_status::timeout
,否则返回 cv_status::no_timeout
。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
![]() |
注意 |
---|---|
timed_wait 的 duration 重载很难正确使用。 在大多数情况下,应优先使用接受谓词的重载。 |
如同
while(!pred()) { if(!wait_until(lock,abs_time)) { return pred(); } } return true;
如同
return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred));
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 pred)
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 pred)
template <class lock_type, class Clock, class Duration> cv_status wait_until(lock_type& lock, const chrono::time_point<Clock, Duration>& abs_time)
template <class lock_type, class Rep, class Period> cv_status wait_for(lock_type& lock, const chrono::duration<Rep, Period>& rel_time)
template <class lock_type, class Clock, class Duration, class Predicate> bool wait_until(lock_type& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred)
template <class lock_type, class Rep, class Period, class Predicate> bool wait_for(lock_type& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred)
//#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 }; }
构造一个 condition_variable_any
类的对象。
如果发生错误,则抛出 boost::thread_resource_error
异常。
通过调用 notify_one
或 notify_all
(即使对 wait
或 timed_wait
的相应调用尚未返回) 已经通知了所有正在 *this
上等待的线程。
销毁对象。
无。
如果有任何线程当前在调用 wait
或 timed_wait
时被 阻塞 在 *this
上,则解除阻塞其中一个线程。
无。
如果有任何线程当前在调用 wait
或 timed_wait
时被 阻塞 在 *this
上,则解除阻塞所有这些线程。
无。
原子性地调用 lock.unlock()
并阻塞当前线程。 当调用 this->notify_one()
或 this->notify_all()
通知时,或虚假唤醒时,线程将被解除阻塞。 当线程被解除阻塞(无论什么原因),在 wait
调用返回之前,通过调用 lock.lock()
重新获取锁。 如果函数因异常退出,也会通过调用 lock.lock()
重新获取锁。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
如同
while(!pred()) { wait(lock); }
原子性地调用 lock.unlock()
并阻塞当前线程。 当调用 this->notify_one()
或 this->notify_all()
通知时,或者当 boost::get_system_time()
报告的时间等于或晚于指定的 abs_time
时,或虚假唤醒时,线程将被解除阻塞。 当线程被解除阻塞(无论什么原因),在 wait
调用返回之前,通过调用 lock.lock()
重新获取锁。 如果函数因异常退出,也会通过调用 lock.lock()
重新获取锁。
如果调用因为到达 abs_time
指定的时间而返回,则返回 false
,否则返回 true
。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
原子性地调用 lock.unlock()
并阻塞当前线程。 当调用 this->notify_one()
或 this->notify_all()
通知时,在 rel_time
参数指示的时间段过去之后,或虚假唤醒时,线程将被解除阻塞。 当线程被解除阻塞(无论什么原因),在 wait
调用返回之前,通过调用 lock.lock()
重新获取锁。 如果函数因异常退出,也会通过调用 lock.lock()
重新获取锁。
如果调用因为 rel_time
指定的时间段已经过去而返回,则返回 false
,否则返回 true
。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
![]() |
注意 |
---|---|
timed_wait 的 duration 重载很难正确使用。 在大多数情况下,应优先使用接受谓词的重载。 |
如同
while(!pred()) { if(!timed_wait(lock,abs_time)) { return pred(); } } return true;
原子地调用 lock.unlock()
并阻塞当前线程。当调用 this->notify_one()
或 this->notify_all()
通知时,或者当 Clock::now()
报告的时间等于或晚于指定的 abs_time
时,或者虚假唤醒时,线程将被解除阻塞。当线程被解除阻塞(无论何种原因)后,在 wait
返回前,将通过调用 lock.lock()
重新获取锁。如果函数抛出异常退出,也会通过调用 lock.lock()
重新获取锁。
如果因为到达 abs_time
指定的时间而返回,则返回 cv_status::timeout
,否则返回 cv_status::no_timeout
。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
原子性地调用 lock.unlock()
并阻塞当前线程。 当调用 this->notify_one()
或 this->notify_all()
通知时,在 rel_time
参数指示的时间段过去之后,或虚假唤醒时,线程将被解除阻塞。 当线程被解除阻塞(无论什么原因),在 wait
调用返回之前,通过调用 lock.lock()
重新获取锁。 如果函数因异常退出,也会通过调用 lock.lock()
重新获取锁。
如果因为到达 abs_time
指定的时间而返回,则返回 cv_status::timeout
,否则返回 cv_status::no_timeout
。
lock
必须由当前线程锁定。
如果发生错误,则抛出 boost::thread_resource_error
异常。 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
异常,该调用位于与当前执行线程关联的 boost::thread
对象上。
![]() |
注意 |
---|---|
timed_wait 的 duration 重载很难正确使用。 在大多数情况下,应优先使用接受谓词的重载。 |
如同
while(!pred())
{
if(!wait_until
(lock,abs_time))
{
return pred();
}
}
return true;
如同
return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred));
// #include <boost/thread/condition.hpp> namespace boost { typedef condition_variable_any condition; }
typedef condition
提供此类型定义是为了与之前版本的 boost 兼容。
notify_all_at_thread_exit
()// #include <boost/thread/condition_variable.hpp> namespace boost { void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk); }
调用线程锁定了 lk
,并且没有其他线程在等待 cond
,或者对于所有并发等待(通过 wait
、wait_for
或 wait_until
)的线程提供的每个锁参数,lk.mutex()
返回相同的值。
将与 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 }
![]() |
警告 |
---|---|
仅在支持可变参数模板的 C++11 编译器上提供可变参数原型,否则接口限制为最多 3 个参数。 |
![]() |
警告 |
---|---|
仅在支持 SFINAE 表达式、decltype N3276 和 auto 的 C++11 编译器上才能确保移动语义。正在等待一个可移动的 boost::bind。 |
boost::call_once
提供了一种机制,用于确保初始化例程只运行一次,而不会发生数据竞争或死锁。
once_flag
#ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11 struct once_flag { constexpr 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_THREAD_PROVIDES_ONCE_CXX11,则 boost::once_flag
类型的对象应使用 BOOST_ONCE_INIT
初始化
boost::once_flag f=BOOST_ONCE_INIT;
call_once
template<typename Function, class ...ArgTypes> inline void call_once(once_flag& flag, Function&& f, ArgTypes&&... args);
Function
和每个 ArgTypes
都是 MoveConstructible
,并且 invoke(decay_copy(boost::forward<Function>(f)), decay_copy(boost::forward<ArgTypes>(args))...)
应该格式良好。
对同一个 once_flag
对象调用 call_once
是串行化的。 如果在同一个 once_flag
对象上之前没有有效的 call_once
调用,则会像调用 invoke(decay_copy(boost::forward<Function>(f)), decay_copy(boost::forward<ArgTypes>(args))...)
一样调用参数 func
,并且当且仅当 invoke(decay_copy(boost::forward<Function>(f)), decay_copy(boost::forward<ArgTypes>(args))...)
返回且没有抛出异常时,call_once
的调用才生效。如果抛出异常,则该异常会传播给调用者。如果之前在同一个 once_flag
对象上存在有效的 call_once
调用,则 call_once
会返回而不调用 func
。
对 once_flag
对象上有效的 call_once
调用的完成,与对同一个 once_flag
对象的所有后续 call_once
调用同步。
当效果无法实现,或从 func
传播任何异常时,抛出 thread_resource_error
。
传递给 call_once
的函数不能同时调用 call_once
并传递同一个 once_flag
对象。 这可能会导致死锁,或第二次调用传递的函数。 另一种选择是允许第二次调用立即返回,但这假设代码知道它已被递归调用,并且即使对 call_once
的调用实际上并未调用该函数,也可以继续执行,在这种情况下,它也可以避免递归调用 call_once
。
在某些编译器上,此函数有一些限制,例如,如果不支持可变参数模板,则参数数量限制为 3。
void call_once(void (*func)(),once_flag& flag);
提供第二个重载是为了向后兼容,并且已弃用。 call_once(func,flag)
的效果应与 call_once(flag,func)
的效果相同。
屏障是一个简单的概念。 也被称为汇合点,它是多个线程之间的同步点。 屏障配置为特定数量的线程 (n
),并且当线程到达屏障时,它们必须等待直到所有 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);
构造一个用于 count
个线程的屏障。
如果发生错误,则抛出 boost::thread_resource_error
异常。
barrier(unsigned int count, F&& completion);
完成函数调用 completion()
的结果类型是 void
或 unsigned int
。
构造一个用于 count
个线程和一个完成函数 completion
的屏障。
如果发生错误,则抛出 boost::thread_resource_error
异常。
bool wait();
阻塞,直到 count
个线程在 *this
上调用 wait
或 count_down_and_wait
。 当第 count
个线程调用 wait
时,屏障会被重置,并且所有等待线程都会被解除阻塞。 重置取决于屏障是否使用完成函数构造。 如果没有完成函数或者完成函数的结果是 void,则重置包括恢复原始计数。 否则,重置包括分配完成函数的结果(必须不为 0)。
对于每批等待线程中的恰好一个线程为 true
,否则为 false
。
- 如果发生错误,则抛出 boost::thread_resource_error
。
- 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
,该调用位于与当前执行线程关联的 boost::thread
对象上。
wait()
是一个中断点。
void count_down_and_wait();
阻塞,直到 count
个线程在 *this
上调用 wait
或 count_down_and_wait
。 当第 count
个线程调用 wait
时,屏障会被重置,并且所有等待线程都会被解除阻塞。 重置取决于屏障是否使用完成函数构造。 如果没有完成函数或者完成函数的结果是 void,则重置包括恢复原始计数。 否则,重置包括分配完成函数的结果(必须不为 0)。
- 如果发生错误,则抛出 boost::thread_resource_error
。
- 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
,该调用位于与当前执行线程关联的 boost::thread
对象上。
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(); };
latch 维护一个内部计数器,该计数器在创建 latch 时初始化。一个或多个线程可能会阻塞等待,直到计数器递减到 0。
latch
的实例不可复制或移动。
void wait();
阻塞调用线程,直到内部计数达到零值。 然后,所有等待线程都被解除阻塞。
- 如果发生错误,则抛出 boost::thread_resource_error
。
- 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
,该调用位于与当前执行线程关联的 boost::thread
对象上。
wait()
是一个中断点。
bool try_wait();
如果内部计数为 0,则返回 true,否则返回 false。 不会阻塞调用线程。
- 如果发生错误,则抛出 boost::thread_resource_error
。
template <class Rep, class Period> cv_status wait_for(const chrono::duration<Rep, Period>& rel_time);
阻塞调用线程,直到内部计数达到零值或经过 duration 时间。 如果没有超时,所有等待线程都会被解除阻塞。
如果内部计数为 0,则为 cv_status::no_timeout,如果经过 duration 时间,则为 cv_status::timeout。
- 如果发生错误,则抛出 boost::thread_resource_error
。
- 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
,该调用位于与当前执行线程关联的 boost::thread
对象上。
wait_for()
是一个 中断点。
template <class lock_type, class Clock, class Duration> cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time);
阻塞调用线程,直到内部计数达到零值或到达 time_point。 如果没有超时,所有等待线程都会被解除阻塞。
如果内部计数为 0,则为 cv_status::no_timeout,如果到达 time_point,则为 cv_status::timeout。
- 如果发生错误,则抛出 boost::thread_resource_error
。
- 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
,该调用位于与当前执行线程关联的 boost::thread
对象上。
wait_until()
是一个 中断点。
void count_down();
内部计数器非零。
将内部计数减 1,然后返回。 如果计数达到 0,则将释放在 wait() 中阻塞的任何线程。
- 如果发生错误,则抛出 boost::thread_resource_error
。
- 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
,该调用位于与当前执行线程关联的 boost::thread
对象上。
count_down()
是一个 中断点。
void count_down_and_wait();
内部计数器非零。
将内部计数减 1。 如果结果计数不为 0,则阻塞调用线程,直到一个或多个其他线程通过调用 count_down() 或 count_down_and_wait() 将内部计数递减为 0。
- 如果发生错误,则抛出 boost::thread_resource_error
。
- 如果等待被调用 interrupt()
中断,则抛出 boost::thread_interrupted
,该调用位于与当前执行线程关联的 boost::thread
对象上。
count_down_and_wait()
是一个中断点。
[
reset( size_t );
只有在当前没有其他线程在等待函数中时才能调用此函数。
使用新的初始线程计数值重置 latch。
- 如果发生错误,则抛出 boost::thread_resource_error
。
]
![]() |
警告 |
---|---|
这些功能是实验性的,可能会在未来的版本中更改。 还没有太多的测试,所以你可能会发现一些小的错误 :( |
![]() |
注意 |
---|---|
这些功能基于 Chris Mysen、Niklas Gustafsson、Matt Austern、Jeffrey Yasskin 的 N3785 - 执行器和调度器修订版 3 C++1y 提案。 以下文本改编自本文,以显示差异。 |
执行器是可以执行打包为函数对象的工作单元的对象。 Boost.Thread 与 N3785 的主要区别在于,执行器不需要继承自抽象类 Executor。 而是使用静态多态性,并在内部使用类型擦除。
多线程程序通常涉及以异步方式执行的离散(有时很小)的工作单元。 这通常涉及将工作单元传递给管理执行的某些组件。 我们已经有 boost::async,它可以异步执行一个函数,并最终在 future 中返回其结果。 (“仿佛”通过启动一个新线程。)
如果存在常规的小工作项流,那么我们几乎肯定不想为每个工作项启动一个新线程,并且我们可能至少希望控制哪些线程执行哪些工作项。 通常方便地将该控制表示为多个执行器对象。 这允许程序在必要时启动执行器,从一个执行器切换到另一个执行器以控制执行策略,并使用多个执行器来防止干扰和线程耗尽。 执行器类存在几种可能的实现,并且实际上存在许多执行器主要组,这些执行器已被发现在实际代码中很有用(存在更多实现,这只是它们的高级分类)。 它们沿几个主要维度有所不同:将使用多少个执行上下文,如何选择它们以及如何对其进行优先级排序。
一个问题是,该库应该包含哪些执行器(或其他的)。这些以及许多其他执行器都有用例。通常,拥有多个已实现的执行器(例如,线程池)是有用的,以便更精确地控制工作执行的位置,因为存在 GUI 线程,或者用于测试目的。一些核心执行器经常有用,这里已经概述了它们,作为该库应该包含的核心内容。如果出现备选执行器实现的常见用例,可以在将来添加它们。目前提供的集合包括:一个基本线程池 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.Thread 的作者采用了与 N3785 不同的方法。我们没有将所有设计都基于抽象执行器类,而是使用了执行器概念。我们认为这是一个好的方向,因为静态多态执行器可以看作是使用简单适配器的动态多态执行器。我们也相信,这将使该库更易于使用,并且对用户来说更方便。
主要的设计决策在于确定工作单元是什么,如何在多态方式下管理工作单元和时间相关函数。
执行器是一个调度已提交给它的闭包的对象,通常是异步的。执行器类可能存在多种模型。一些具体的设计要点:
一个重要的问题是闭包到底是什么。该库有一个非常简单的答案:闭包是一个没有参数并返回 void
的 Callable
。
N3785 选择了更具体的 std::function<void()>
,因为它只提供动态多态,并且声明在实践中,基于模板的方法或另一种方法是不切实际的。该库的作者认为,基于模板的方法与基于动态的方法兼容。他们给出了一些论据:
首先,虚函数不能是模板。这是真的,但执行器接口可以提供调用虚拟公共函数的模板函数也是真的。他们给出的另一个原因是“模板参数会使接口复杂化,而不会增加任何真正的通用性。最终,执行器类将需要某种类型擦除来处理所有具有 void()
签名的不同类型的函数对象,而这正是 std::function 已经做的事情”。我们认为,管理这些实现细节是执行器的责任,而不是用户的责任。
我们同意他们给出的所有与工作单元的 void()
接口相关的论点。工作单元是一个不带参数且不返回值的闭包。这确实是对用户代码的限制,但结合将执行器作为参数的 boost::async
,用户拥有一切所需。
第三个与性能有关。他们断言“用于在执行器队列上存储闭包的任何机制都必须使用某种形式的类型擦除。没有理由相信,专门为 std::executor 编写且在标准库中其他任何地方都没有使用的自定义闭包机制在这方面会比 std::function<void()>
更好”。我们认为,实现可以做得更好,而不是将闭包存储在 std::function<void()>
上。例如,该实现可以使用侵入式数据来存储闭包和存储给定顺序的闭包所需的其他节点的指针。
此外,std::function<void()>
无法通过移动闭包来构造,因此,例如 std::packaged_task
不能是闭包。
与 N3785 提案相比,这个库在计划工作方面的方法截然不同。我们没有将计划操作添加到特定的 scheduled_executor 多态接口,而是选择添加一个不是执行器的特定 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
任务。请注意,不同时钟上的持续时间有所不同。
与 N3785 类似,并且基于与 std
/boost::thread
相同的设计决策,如果用户闭包引发异常,则执行器必须调用 std::terminate
函数。请注意,当我们组合 boost::async
和 Executors
时,异常将被与返回的 future 关联的闭包捕获,因此异常将存储在返回的 future 中,与其他 async
重载一样。
一种常见的习惯是在线程开始时设置一些线程局部变量。由于执行器可以在内部实例化线程,因此这些执行器应能够在执行器构造函数中调用用户特定的线程入口函数。
对于不实例化任何线程且将使用当前线程的执行器,此函数应仅为调用 at_thread_entry
成员函数的线程调用。
该库尚未提供取消/中断工作的能力,尽管这是一个常见的请求功能。
这可以由一个额外的取消对象在外部进行管理,该取消对象可以在工作单元的创建者和工作单元之间共享。
我们也可以考虑一种可取消的闭包,它可以用更透明的方式使用。
另一种选择是使async返回一个cancelable_task,但这同时也需要一个可取消的闭包。
该库没有提供获取当前执行器的能力,但如果能够访问它,可以大大简化用户代码。
原因是用户总是可以使用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); } );
[
该库的作者与 C++ 标准委员会存在一些相同的担忧(引入一个新的单一共享资源,一个单例,可能会使其难以移植到所有环境),并且该库目前不需要提供默认执行器。
用户总是可以自己定义默认执行器。
boost::generic_executor_ref default_executor() { static boost::basic_thread_pool tp(4); return generic_executor_ref(tp); }
如果类型 E
是 Callable(void())
和 CopyConstructible
/MoveConstructible
的模型,则满足 Closure
要求。
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
表示 Closure
类型的左值引用。rc
表示 Closure
类型的右值引用。p
表示 Predicate
类型的值。
指定的闭包将在未来的某个时间点安排执行。 如果调用的闭包抛出异常,执行器将调用 std::terminate,就像线程一样。
特定线程上闭包的完成发生在线程的线程局部变量销毁之前。
void
.
如果线程池已关闭,则 sync_queue_is_closed。 存储闭包时可能抛出的任何异常。
如果抛出异常,则执行器状态未修改。
指定的闭包将在未来的某个时间点安排执行。 如果调用的闭包抛出异常,执行器将调用 std::terminate,就像线程一样。
特定线程上闭包的完成发生在线程的线程局部变量销毁之前。
void
.
如果线程池已关闭,则 sync_queue_is_closed。 存储闭包时可能抛出的任何异常。
如果抛出异常,则执行器状态未修改。
关闭执行器 e
以进行提交。
工作线程将一直工作到没有更多闭包要运行为止。
void
.
确保线程安全时可能抛出的任何异常。
如果抛出异常,则执行器状态未修改。
bool
.
执行器是否已关闭以进行提交。
确保线程安全时可能抛出的任何异常。
尝试执行一项工作。
是否已执行一项工作。
bool
.
是否已执行一项工作。
无论当前工作构造函数抛出什么,或者 work()
抛出什么。
必须从计划的工作中调用此方法
重新安排工作直到 p()
。
bool
.
是否已执行一项工作。
无论当前工作构造函数抛出什么,或者 work()
抛出什么。
#include <boost/thread/executors/work.hpp> namespace boost { typedef 'implementation_defined' work; }
work 是 'Closure' 的模型
执行器抽象基类。
#include <boost/thread/executors/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 到执行器的多态适配器。
#include <boost/thread/executors/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);
构造一个 executor_adaptor。
无。
virtual ~executor_adaptor();
销毁 executor_adaptor。
所有闭包的完成发生在执行器析构函数完成之前。
执行器抽象基类。
#include <boost/thread/executors/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
不是 Executor。
#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); }; }
template <class Clock, class Duration, typename Closure> void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
安排 closure
在 abs_time
执行。
无。
template <class Rep, class Period, typename Closure> void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
安排一个 closure
在 rel_time
之后执行。
无。
#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);
构造一个 at_executor
。
无。
template <typename Closure> void submit(Closure&& closure);
安排 closure
在构造时给定的 abs_time
执行。
无。
template <class Clock, class Duration, typename Closure> void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
安排 closure
在 abs_time
执行。
无。
template <class Rep, class Period, typename Closure> void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
安排一个 closure
在 rel_time
之后执行。
无。
#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);
构造一个 scheduler_executor_wrapper
。
无。
~scheduler_executor_wrapper();
销毁 scheduler_executor_wrapper
。
所有闭包的完成发生在执行器析构函数完成之前。
template <typename Closure> void submit(Closure&& closure);
在底层执行器上提交 closure
。
无。
template <class Clock, class Duration, typename Closure> void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
重新提交 closure
以在底层执行器上于 abs_time
执行。
无。
template <class Rep, class Period, typename Closure> void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
重新提交 closure
以在底层执行器上于 rel_time
之后执行。
无。
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);
构造一个 resubmit_at_executor
。
无。
~resubmit_at_executor();
销毁 executor_adaptor。
所有闭包的完成发生在执行器析构函数完成之前。
template <typename Closure> void submit(Closure&& closure);
重新提交 closure
以在底层执行器上于构造时给定的 abs_time
执行。
无。
template <class Clock, class Duration, typename Closure> void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
重新提交 closure
以在底层执行器上于 abs_time
执行。
无。
template <class Rep, class Period, typename Closure> void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
重新提交 closure
以在底层执行器上于 rel_time
之后执行。
无。
一个串行执行器,确保没有两个工作单元同时执行。
#include <boost/thread/executors/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);
构造一个 serial_executor。
无。
一个串行执行器,确保没有两个工作单元同时执行。
#include <boost/thread/executors/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); }; }
一个具有最多固定数量线程的线程池。
#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); }; }
创建一个线程池,该线程池在 thread_count
线程上运行 closures。
初始化所需资源时抛出的任何异常。
一个为每个任务创建一个线程的 thread_executor。
#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); }; }
创建一个 thread_executor。
初始化所需资源时抛出的任何异常。
~thread_executor();
等待 closures (如果有) 完成,然后连接并销毁线程。
所有闭包的完成发生在执行器析构函数完成之前。
一个用户调度的执行器。
#include <boost/thread/executors/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();
创建一个执行器,该执行器使用其 closure 执行方法之一运行 closures。
初始化所需资源时抛出的任何异常。
void run_queued_closures();
重新调度已排队的工作。
无论当前工作构造函数抛出什么,或者 work()
抛出什么。
futures 库提供了一种处理同步 future 值的方法,无论这些值是由另一个线程生成,还是在单个线程上响应外部刺激,或者按需生成。
这是通过提供四个类模板来实现的:future
和 boost::shared_future
用于检索异步结果,以及 boost::promise
和 boost::packaged_task
用于生成异步结果。
future
的一个实例拥有对结果的唯一引用。所有权可以在实例之间使用移动构造函数或移动赋值运算符进行转移,但最多只有一个实例拥有对给定异步结果的引用。 当结果准备好时,它会通过 rvalue 引用从 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
,该 future 将包含任务的结果。
您可以单独等待 future,也可以使用 boost::wait_for_any()
和 boost::wait_for_all()
函数之一等待。
您可以使用 boost::promise
或 boost::packaged_task
设置 future 中的值。 boost::packaged_task
是一个可调用对象,它包装一个函数或可调用对象。 调用 packaged task 时,它会依次调用包含的函数,并使用返回值填充 future。 这是对长期存在的问题“如何从线程返回值?”的回答:将您希望运行的函数打包为 boost::packaged_task
并将 packaged task 传递给线程构造函数。 然后可以使用从 packaged task 检索的 future 来获取返回值。 如果该函数抛出异常,则会将其存储在 future 中以代替返回值。
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
稍微低级一些:它只提供显式函数来将值或异常存储在关联的 future 中。 因此,当值可能来自多个可能的源,或者单个操作可能产生多个值时,可以使用 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
的结果的 future 上时,该回调在执行等待的线程中。 可以使用相应的 boost::promise
或 boost::packaged_task
上的 set_wait_callback()
成员函数来设置这些回调。
这允许 延迟 future,其中直到某些线程需要结果时才实际计算结果。 在下面的示例中,对 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
完成其工作 - 例如等待 future 准备就绪 - 那么线程特定变量的析构函数仍然会在等待线程恢复后运行。 本节介绍如何使用标准机制使此类同步安全,方法是确保在 future 准备就绪之前销毁具有线程存储持续时间的对象。 例如:
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()
的调用返回时,我们知道不仅 future 值准备就绪,而且另一个线程上的线程特定变量也已被销毁。
为此,为 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
其他线程可以等待从 task 获取的 future,而不必担心由于 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); }
等待线程可以保证在调用 process(the_data)
之前,已经销毁了 thread_func()
使用的线程特定对象。 如果在设置 data_ready
之后和调用 boost::notify_all_at_thread_exit()
之前释放并重新获取 m
上的锁,则这不成立,因为线程可能会由于虚假唤醒而从等待中返回。
boost::async
提供了一种简单的方式来运行异步任务,以利用可用的硬件并发性。调用 boost::async
会返回一个 boost::future
,该 future 将包含任务的结果。根据启动策略,任务可以在其自身的线程上异步运行,也可以在调用该 future 的 wait()
或 get()
成员函数的线程上同步运行。
启动策略可以是 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
被设计为在线程之间共享,即允许多个并发的 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 }
声明 shared_future
时需要指定返回类型;auto 在模板参数列表中不可用。这里可以使用 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 }
用户可以读取或写入 future 变量。
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
变量,即线程安全的,以及一个可以在多个线程之间共享的 shared_future
,但是没有足够的共识和时间来为 C++11 做好准备。
某些函数可能在构造时就知道该值。在这些情况下,该值立即可用,但需要作为 future 或 shared_future 返回。通过使用 make_ready_future,可以创建一个 future,该 future 在其共享状态中保存预先计算的结果。
如果没有这些功能,则很难直接从值创建 future。首先必须创建一个 promise,然后设置该 promise,最后从该 promise 中检索 future。现在可以通过一个操作完成。
此函数为给定值创建一个 future。如果没有给出值,则返回 future<void>。此函数主要在以下情况下有用:有时,返回值立即可用,但有时则不可用。下面的示例说明,在错误路径中,该值是立即知道的,但是在其他路径中,该函数必须返回一个表示为 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; }
此函数有两个变体。第一个采用任何类型的值,并返回该类型的 future。输入值被传递到返回的 future 的共享状态。第二个版本不接受任何输入,并返回 future<void>。
在异步编程中,一个异步操作在完成时调用第二个操作并将数据传递给它非常常见。当前的 C++ 标准不允许将延续注册到 future。 使用 .then
,无需等待结果,而是将延续“附加”到异步操作,并在结果准备就绪时调用该延续。使用 .then
函数注册的延续将有助于避免阻塞等待或在轮询上浪费线程,从而大大提高应用程序的响应能力和可伸缩性。
future.then()
提供了通过声明一个 future 是另一个 future 的延续来顺序组合两个 future 的能力。使用 .then()
,在 lambda 函数指示延续开始之前,先前的 future 已经准备好(在共享状态中存储了值或异常)。
在下面的示例中,使用 .then()
成员函数将 future<string>
f2
注册为 future<int>
f1
的延续。此操作接受一个 lambda 函数,该函数描述了在 f1
准备就绪后 f2
应如何进行。
#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 产生一个延续接受作为输入的值。通过使用 future.then()
,创建延续链变得简单直观
myFuture.then(...).then(...).then(...).
一些需要注意的点是
输入参数
.then
提供一个重载以接受执行器引用,使程序员可以灵活地控制 future 的执行。如上所述,通常采用启动策略不足以进行强大的异步操作。执行器的生命周期必须超过延续。返回值:返回 future 的决定主要基于使用 .then()
链接多个延续的能力。组合性的这种好处使程序员可以对其代码进行令人难以置信的控制和灵活性。返回 future
对象而不是 shared_future
也是一种更便宜的操作,从而提高了性能。shared_future
对象不是利用链接功能所必需的。当需要时,也可以很容易地使用 future::share() 从 future
转到 shared_future
。
state
future_errc
launch
is_error_code_enum<future_errc>
make_error_code()
make_error_condition()
future_category()
future_error
future_status
exceptional_ptr
EXPERIMENTALfuture
类模板shared_future
类模板promise
类模板packaged_task
类模板decay_copy()
async()
wait_for_any()
- EXTENSIONwait_for_all()
- EXTENSIONwhen_all()
- EXTENSIONwhen_any()
- EXTENSIONmake_ready_future()
EXTENSIONmake_exceptional_future()
EXTENSIONmake_future()
DEPRECATEDmake_shared_future()
DEPRECATED//#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 };
枚举类型 launch 是一个位掩码类型,其中 launch::async 和 launch::deferred 表示单独的位。
使用 promise<>
或 packaged_task<>
或 make_ready_future
/make_exceptional_future
创建的 future(没有关联的启动策略),具有隐式的启动策略 launch::none
。
由 async(launch::async, ...)
或 ::then(launch::async, ...)
创建的 future 具有关联的启动策略 launch::async
。由 async(launch::deferred, ...)
或 ::then(launch::deferred, ...)
创建的 future 具有关联的启动策略 launch::deferred
。由 async(Executor, ...)
或 ::then(Executor, ...)
或 ::then(launch::executor, ...)
创建的 future 具有关联的启动策略 launch::executor
。由 async(...)
或 ::then(...)
创建的 future 具有关联的启动策略 launch::none
。
由 ::then(launch::inherit, ...)
创建的 future 具有与父 future 关联的启动策略。
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); }
error_code(static_cast<int>(e), future_category())
.
namespace system { error_condition make_error_condition(future_errc e); }
error_condition(static_cast<int>(e), future_category())
.
const system::error_category& future_category();
对从 error_category 类派生的类型的对象的引用。
对象的 default_error_condition
和等效虚拟函数的行为与 system::error_category
类的指定行为相同。对象的 name
虚拟函数返回指向字符串 "future" 的指针。
class future_error : public std::logic_error { public: future_error(system::error_code ec); const system::error_code& code() const no_except; };
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);
传递给构造函数的异常,或者如果没有参数移动到构造的 exceptional_ptr
中(如果是右值),则为当前异常。 否则,异常会被复制到构造的 exceptional_ptr
中。
valid() == true && is_ready() = true && has_value() = false
无。
template <typename R> classfuture
{ public: typedef R value_type; // EXTENSIONfuture
(future
const& rhs) = delete;future
& operator=(future
const& rhs) = delete;future
() noexcept; ~future
(); // move supportfuture
(future
&& other) noexcept; explicitfuture
(future
<future
<R>>&& rhs); // EXTENSIONfuture
& 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 Ex, 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(); // EXTENSIONfuture
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
();
构造一个未初始化的 future
。
this->is_ready
返回 false
。 this->get_state()
返回 boost::future_state::uninitialized
。
无。
future
(future
&& other);
构造一个新的 future
,并将与 other
关联的共享状态的所有权转移到 *this
。
this->get_state()
返回调用前 other->get_state()
的值。other->get_state()
返回 boost::future_state::uninitialized
。如果 other
与共享状态相关联,则该结果现在与 *this
相关联。other
不与任何共享状态相关联。
无。
如果编译器不支持右值引用,则使用 boost.thread 移动模拟来实现。
explicitfuture
(future
<future
<R>>&& other); // EXTENSION
![]() |
警告 |
---|---|
此构造函数是实验性的,并且在未来的版本中可能会更改。目前还没有太多的测试,所以你可能会发现一些小 bug :( |
other.valid()
.
[影响
构造一个新的 future
,并将与 other
关联的共享状态的所有权转移,并解包内部 future(参见 unwrap()
)。
this->get_state()
返回调用前的 other->get_state()
的值。 other->get_state()
返回 boost::future_state::uninitialized
。 关联的共享状态现在被解包,内部 future 共享状态与 *this
关联。 other
不与任何共享状态关联,! other.valid()
。
无。
如果编译器不支持右值引用,则使用 boost.thread 移动模拟来实现。
future
& operator=(future
&& other);
将与 other
关联的共享状态的所有权转移给 *this
。
this->get_state()
返回调用前的 other->get_state()
的值。 other->get_state()
返回 boost::future_state::uninitialized
。 如果 other
与共享状态关联,则该结果现在与 *this
关联。 other
不与任何共享状态关联。 如果在调用前 *this
与异步结果关联,则该结果不再具有关联的 future
实例。
无。
如果编译器不支持右值引用,则使用 boost.thread 移动模拟来实现。
void swap( future
& other) no_except;
交换与 other
和 *this
关联的共享状态的所有权。
this->get_state()
返回调用前的 other->get_state()
的值。 other->get_state()
返回调用前的 this->get_state()
的值。 如果 other
与共享状态关联,则该结果现在与 *this
关联,否则 *this
没有关联的结果。 如果 *this
与共享状态关联,则该结果现在与 other
关联,否则 other
没有关联的结果。
无。
R get(); R&future
<R&>::get(); voidfuture
<void>::get();
如果 *this
与共享状态关联,则等待结果准备就绪,如同调用 boost::future<R>::wait()
一样,并检索结果(无论是值还是异常)。
-
返回存储的引用。future
<R&>::get()
-
,没有返回值。future
<void>::get()
-
返回对存储在共享状态中的值的右值引用。future
<R>::get()
this->is_ready()
返回 true
。 this->get_state()
返回 boost::future_state::ready
。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 任何存储在共享状态中以代替值的异常。
get()
是一个中断点。
R get_or(R&& v); // EXTENSION R get_or(R const& v); // EXTENSION R&future
<R&>::get_or(R& v); // EXTENSION voidfuture
<void>::get_or(); // EXTENSION
![]() |
警告 |
---|---|
这些函数是实验性的,并且在未来的版本中可能会发生更改。 目前没有太多的测试,因此您可能会发现一些小错误 :( |
如果 *this
与共享状态关联,则等待结果准备就绪,如同调用 boost::future<R>::wait()
一样,并根据共享状态 has_value()
来检索结果。
-
如果 has_value() 则返回存储的引用,否则返回传递的参数。future
<R&>::get_or(v)
-
,没有返回值,但即使共享状态包含异常,该函数也不会抛出异常。future
<void>::get_or()
-
如果 future
<R>::get_or(v)has_value()
则返回对存储在共享状态中的值的右值引用,并返回使用参数 v
构建的右值引用。
this->is_ready()
返回 true
。 this->get_state()
返回 boost::future_state::ready
。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
get_or()
是一个 中断点。
void wait() const;
如果 *this
与共享状态关联,则等待直到结果准备就绪。 如果在进入时结果尚未准备就绪,并且结果设置了等待回调,则在等待之前调用该回调。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
this->is_ready()
返回 true
。 this->get_state()
返回 boost::future_state::ready
。
wait()
是一个中断点。
template<typename Duration> bool timed_wait(Duration const& wait_duration);
![]() |
警告 |
---|---|
自 3.00 起已弃用。 请改用 |
如果 *this
与共享状态关联,则等待直到结果准备就绪,或者经过 wait_duration
指定的时间。 如果在进入时结果未准备好,并且结果设置了等待回调,则在等待之前会调用该回调。
如果 *this
与共享状态关联,并且该结果在指定时间过去之前准备就绪,则为 true
,否则为 false
。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
如果此调用返回 true
,则 this->is_ready()
返回 true
并且 this->get_state()
返回 boost::future_state::ready
。
timed_wait()
是一个中断点。 Duration
必须是满足 Boost.DateTime 时间段要求的类型。
bool timed_wait(boost::system_time const& wait_timeout);
![]() |
警告 |
---|---|
自 3.00 起已弃用。 请改用 |
如果 *this
与共享状态关联,则等待直到结果准备就绪,或者经过 wait_timeout
指定的时间点。 如果在进入时结果未准备好,并且结果设置了等待回调,则在等待之前会调用该回调。
如果 *this
与共享状态关联,并且该结果在指定时间过去之前准备就绪,则为 true
,否则为 false
。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
如果此调用返回 true
,则 this->is_ready()
返回 true
并且 this->get_state()
返回 boost::future_state::ready
。
timed_wait()
是一个中断点。
template <class Rep, class Period> future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
如果 *this
与共享状态关联,则等待直到结果准备就绪,或者经过 wait_duration
指定的时间。 如果在进入时结果未准备好,并且结果设置了等待回调,则在等待之前会调用该回调。
- future_status::deferred
如果共享状态包含延迟函数。(尚未实现)
- future_status::ready
如果共享状态已准备好。
- future_status::timeout
如果函数由于 rel_time
指定的相对超时已过期而返回。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
如果此调用返回 true
,则 this->is_ready()
返回 true
并且 this->get_state()
返回 boost::future_state::ready
。
wait_for()
是一个中断点。 Duration
必须是满足 Boost.DateTime 时间段要求的类型。
template <class Clock, class Duration> future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
如果 *this
与共享状态关联,则等待直到结果准备就绪,或者经过 wait_timeout
指定的时间点。 如果在进入时结果未准备好,并且结果设置了等待回调,则在等待之前会调用该回调。
- future_status::deferred
如果共享状态包含延迟函数。(尚未实现)
- future_status::ready
如果共享状态已准备好。
- future_status::timeout
如果函数由于达到 absl_time
指定的绝对超时而返回。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
如果此调用返回 true
,则 this->is_ready()
返回 true
并且 this->get_state()
返回 boost::future_state::ready
。
wait_until()
是一个 中断点。
bool valid() const noexcept;
如果 *this
与共享状态关联,则为 true
,否则为 false
。
此函数的结果不稳定,即使该函数返回 true 或反之亦然,future 也可能变为无效。
无。
bool is_ready() const;
如果 *this
与共享状态关联,并且该结果已准备好进行检索,则为 true
,否则为 false
。
此函数的结果不稳定,即使该函数返回 true 或反之亦然,future 也可能变为未就绪。
无。
bool has_value() const;
如果 *this
与共享状态关联,并且该结果已准备好进行检索,并且该结果是存储的值,则为 true
,否则为 false
。
此函数的结果不稳定,即使该函数返回 true 或反之亦然,future 也可能丢失其值。
无。
bool has_exception() const;
如果 *this
与共享状态关联,并且该结果已准备好进行检索,并且该结果是存储的异常,则为 true
,否则为 false
。
此函数的结果不稳定,即使该函数返回 true 或反之亦然,future 也可能丢失其异常。
无。
exception_ptr get_exception_ptr();
如果 *this
与共享状态关联,则等待直到结果准备就绪。 如果在进入时结果尚未准备就绪,并且结果设置了等待回调,则在等待之前调用该回调。
一个 exception_ptr,存储或不存储异常。
此函数的结果不稳定,即使该函数返回有效的 exception_ptr
或反之亦然,future 也可能丢失其异常。
mutex::lock()/mutex::unlock()
可能抛出的任何异常。
future_state::state get_state();
确定与 *this
关联的共享状态(如果有)的状态。
如果 *this
未与共享状态关联,则为 boost::future_state::uninitialized
。 如果与 *this
关联的共享状态已准备好进行检索,则为 boost::future_state::ready
,否则为 boost::future_state::waiting
。
此函数的结果不稳定。
无。
shared_future<R> share();
shared_future<R>(boost::move(*this))
.
this->valid() == false
.
template<typename F>future
<typename boost::result_of<F(future
)>::type> then(F&& func); // EXTENSION template<typename Ex, 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
![]() |
警告 |
---|---|
这些函数是实验性的,并且在未来的版本中可能会发生更改。 目前没有太多的测试,因此您可能会发现一些小错误 :( |
![]() |
注意 |
---|---|
这些函数基于 N3634 - Improvements to std::future<T> and related APIs C++1y 提案,作者:N. Gustafsson, A. Laksberg, H. Sutter, S. Mithani。 |
这三个函数的区别仅在于输入参数。 第一个仅接受一个可调用对象,该对象接受 future 对象作为参数。 第二个函数将执行器作为第一个参数,将可调用对象作为第二个参数。 第三个函数将启动策略作为第一个参数,将可调用对象作为第二个参数。
INVOKE(DECAY_COPY (std::forward<F>(func)), std::move(*this))
必须是有效的表达式。
所有函数都会创建一个与返回的 future 对象关联的共享状态。此外,
- 当对象的共享状态准备就绪时,会根据重载(见下文)调用 continuation `INVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this))
,其中对 DECAY_COPY() 的调用会在调用 then 的线程中求值。
- 从 continuation 返回的任何值都作为结果存储在生成的 `future
` 的共享状态中。 从 continuation 的执行传播的任何异常都作为异常结果存储在生成的 `future
` 的共享状态中。
continuation 根据指定的策略或执行器启动,或者不启动。
- 当启动策略为 `launch::none
` 时,continuation 在未指定的执行线程上调用。
- 当启动策略为 `launch::async
` 时,continuation 在新的执行线程上调用。
- 当启动策略为 `launch::deferred
` 时,continuation 按需调用。
- 当启动策略为 `launch::executor
` 时,continuation 在执行器的其中一个执行线程上调用。
- 当启动策略为 `launch::inherit
` 时,continuation 继承父级的启动策略或执行器。
- 当未提供执行器或启动策略时(第一个重载),相当于指定了 launch::none。
- 当提供执行器时(第二个重载),continuation 在执行器的其中一个执行线程上调用。
- 如果父级具有 `launch::deferred
` 策略,并且 continuation 没有指定的启动策略执行器,则通过立即调用 `.wait()
` 填充父级,并且 antecedent 的策略为 `launch::deferred
`。
类型为 `
的对象,它引用由 continuation 创建的共享状态。future
<typename boost::result_of<F( future
)>
- 请注意,嵌套的 future 尚未隐式解包。 这可能会在未来的版本中更改。
- 返回的 future 的行为与 boost::async 返回的 future 相同,从 then 返回的 future 对象的析构函数将阻塞。 这可能会在未来的版本中更改。
- 传递给 continuation 函数参数的 `
对象是原始 `future
的副本。future
- 原始 future 上的 `valid() == false
`;then 返回的 `future
` 上的 `valid() == true
`。
template <typename R2>future
<R2>future
<future
<R2>>::unwrap(); // EXTENSION template <typename R2>boost::shared_future
<R2>future
<boost::shared_future
<R2>>::unwrap(); // EXTENSION
![]() |
警告 |
---|---|
这些函数是实验性的,并且在未来的版本中可能会发生更改。 目前没有太多的测试,因此您可能会发现一些小错误 :( |
![]() |
注意 |
---|---|
这些函数基于 N3634 - Improvements to std::future<T> and related APIs C++1y 提案,作者:N. Gustafsson, A. Laksberg, H. Sutter, S. Mithani。 |
删除最外层的 future,并返回一个 future,其关联状态是外部 future 的代理。
- 返回一个 future,当外部和内部 future 的共享状态准备就绪时,该 future 变为就绪。 无法先验地确定应用于外部 future 的 `get()
` 返回的 future 的有效性。 如果它无效,则此 future 将被强制有效,并因 `future_error
` 类型的异常而变为就绪,错误代码为 `future_errc::broken_promise
`。
类型为 future 的对象,其关联状态是外部 future 的代理。
- 返回的 future 具有 `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();
构造一个未初始化的 shared_future。
`this->is_ready
` 返回 `false
`。 `this->get_state()
` 返回 `boost::future_state::uninitialized
`。
无。
const R& get() const; R& get() const; void get() const;
如果 `*this
` 与共享状态关联,则等待结果准备就绪,就像通过调用 `boost::shared_future<R>::wait()
一样,并返回对结果的 `const
` 引用。
- `shared_future<R&>::get()
` 返回存储的引用。
- `shared_future<void>::get()
`,没有返回值。
- `shared_future<R>::get()
` 返回对存储在共享状态中的值的 `const
` 引用。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
get()
是一个中断点。
void wait() const;
如果 *this
与共享状态关联,则等待直到结果准备就绪。 如果在进入时结果尚未准备就绪,并且结果设置了等待回调,则在等待之前调用该回调。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
`this->is_ready()
` 返回 `true
`。 `this->get_state()
` 返回 `boost::future_state::ready
`。
wait()
是一个中断点。
template<typename Duration> bool timed_wait(Duration const& wait_duration);
如果 *this
与共享状态关联,则等待直到结果准备就绪,或者经过 wait_duration
指定的时间。 如果在进入时结果未准备好,并且结果设置了等待回调,则在等待之前会调用该回调。
如果 *this
与共享状态关联,并且该结果在指定时间过去之前准备就绪,则为 true
,否则为 false
。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
如果此调用返回 `true
`,则 `this->is_ready()
` 返回 `true
`,并且 `this->get_state()
` 返回 `boost::future_state::ready
`。
timed_wait()
是一个中断点。 Duration
必须是满足 Boost.DateTime 时间段要求的类型。
bool timed_wait(boost::system_time const& wait_timeout);
如果 *this
与共享状态关联,则等待直到结果准备就绪,或者经过 wait_timeout
指定的时间点。 如果在进入时结果未准备好,并且结果设置了等待回调,则在等待之前会调用该回调。
如果 *this
与共享状态关联,并且该结果在指定时间过去之前准备就绪,则为 true
,否则为 false
。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
如果此调用返回 `true
`,则 `this->is_ready()
` 返回 `true
`,并且 `this->get_state()
` 返回 `boost::future_state::ready
`。
timed_wait()
是一个中断点。
template <class Rep, class Period> future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
如果 *this
与共享状态关联,则等待直到结果准备就绪,或者经过 wait_duration
指定的时间。 如果在进入时结果未准备好,并且结果设置了等待回调,则在等待之前会调用该回调。
- future_status::deferred
如果共享状态包含延迟函数。(尚未实现)
- future_status::ready
如果共享状态已准备好。
- future_status::timeout
如果函数由于 rel_time
指定的相对超时已过期而返回。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
如果此调用返回 `true
`,则 `this->is_ready()
` 返回 `true
`,并且 `this->get_state()
` 返回 `boost::future_state::ready
`。
timed_wait()
是一个中断点。 Duration
必须是满足 Boost.DateTime 时间段要求的类型。
template <class Clock, class Duration> future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
如果 *this
与共享状态关联,则等待直到结果准备就绪,或者经过 wait_timeout
指定的时间点。 如果在进入时结果未准备好,并且结果设置了等待回调,则在等待之前会调用该回调。
- future_status::deferred
如果共享状态包含延迟函数。(尚未实现)
- future_status::ready
如果共享状态已准备好。
- future_status::timeout
如果函数由于达到 absl_time
指定的绝对超时而返回。
- 如果 *this
不与共享状态关联,则返回 boost::future_uninitialized
。
- 如果在调用时与 *this
关联的结果尚未准备好,并且当前线程被中断,则返回 boost::thread_interrupted
。
- 如果调用了等待回调,则等待回调抛出的任何异常。
如果此调用返回 `true
`,则 `this->is_ready()
` 返回 `true
`,并且 `this->get_state()
` 返回 `boost::future_state::ready
`。
timed_wait()
是一个中断点。
bool is_ready() const;
如果 *this
与一个共享状态相关联,并且该结果准备好被检索,则返回 true
,否则返回 false
。
mutex::lock()/mutex::unlock()
可能抛出的任何异常。
bool has_value() const;
如果 *this
与共享状态关联,并且该结果已准备好进行检索,并且该结果是存储的值,则为 true
,否则为 false
。
mutex::lock()/mutex::unlock()
可能抛出的任何异常。
bool has_exception() const;
如果 *this
与共享状态关联,并且该结果已准备好进行检索,并且该结果是存储的异常,则为 true
,否则为 false
。
mutex::lock()/mutex::unlock()
可能抛出的任何异常。
exception_ptr get_exception_ptr();
如果 *this
与共享状态关联,则等待直到结果准备就绪。 如果在进入时结果尚未准备就绪,并且结果设置了等待回调,则在等待之前调用该回调。
一个 exception_ptr,存储或不存储异常。
mutex::lock()/mutex::unlock()
可能抛出的任何异常。
future_state::state get_state();
确定与 *this
关联的共享状态(如果有)的状态。
如果 *this
未与共享状态关联,则为 boost::future_state::uninitialized
。 如果与 *this
关联的共享状态已准备好进行检索,则为 boost::future_state::ready
,否则为 boost::future_state::waiting
。
mutex::lock()/mutex::unlock()
可能抛出的任何异常。
template<typename F>future
<typename boost::result_of<F(shared_future)>::type> then(F&& func) const; // EXTENSION template<typename Ex, typename F>future
<typename boost::result_of<F(shared_future)>::type> then(Ex& executor, F&& func) const; // EXTENSION template<typename F>future
<typename boost::result_of<F(shared_future)>::type> then(launch policy, F&& func) const; // EXTENSION
![]() |
警告 |
---|---|
这些函数是实验性的,并且在未来的版本中可能会发生更改。 目前没有太多的测试,因此您可能会发现一些小错误 :( |
![]() |
注意 |
---|---|
这些函数基于 N3634 - Improvements to std::future<T> and related APIs C++1y 提案,作者:N. Gustafsson, A. Laksberg, H. Sutter, S. Mithani。 |
这三个函数的区别仅在于输入参数。第一个函数仅接受一个可调用对象,该对象接受一个 shared_future 对象作为参数。第二个函数将执行器作为第一个参数,并将一个可调用对象作为第二个参数。第三个函数将启动策略作为第一个参数,并将一个可调用对象作为第二个参数。
INVOKE(DECAY_COPY (std::forward<F>(func)), *this)
应该是一个有效的表达式。
所有函数都会创建一个与返回的 future 对象关联的共享状态。此外,
- 当对象的共享状态准备就绪时,根据重载(见下文)调用延续 INVOKE(DECAY_COPY(std::forward<F>(func)), *this)
,对 DECAY_COPY() 的调用在调用 then 的线程中进行评估。
- 从 continuation 返回的任何值都作为结果存储在生成的 `future
` 的共享状态中。 从 continuation 的执行传播的任何异常都作为异常结果存储在生成的 `future
` 的共享状态中。
continuation 根据指定的策略或执行器启动,或者不启动。
- 当启动策略为 `launch::none
` 时,continuation 在未指定的执行线程上调用。
- 当启动策略为 `launch::async
` 时,continuation 在新的执行线程上调用。
- 当启动策略为 `launch::deferred
` 时,continuation 按需调用。
- 当启动策略为 `launch::executor
` 时,continuation 在执行器的其中一个执行线程上调用。
- 当启动策略为 `launch::inherit
` 时,continuation 继承父级的启动策略或执行器。
- 当未提供执行器或启动策略时(第一个重载),相当于指定了 launch::none。
- 当提供执行器时(第二个重载),continuation 在执行器的其中一个执行线程上调用。
- 如果父级具有 `launch::deferred
` 策略,并且 continuation 没有指定的启动策略执行器,则通过立即调用 `.wait()
` 填充父级,并且 antecedent 的策略为 `launch::deferred
`。
类型为
的对象,该对象引用由延续创建的共享状态。future
<typename boost::result_of<F(shared_future)>
- 请注意,嵌套的 future 尚未隐式解包。 这可能会在未来的版本中更改。
- 返回的 future 的行为与 boost::async 返回的 future 相同,从 then 返回的 future 对象的析构函数将阻塞。 这可能会在未来的版本中更改。
- future 对象被移动到延续函数的参数中。
- 原始 shared_future
上的 valid() == true
;then 返回的 future
上的 valid() == true
。
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
void set_value_deferred(see below); // EXTENSION
void set_exception_deferred(exception_ptr p); // EXTENSION
template <typename E>
void set_exception_deferred(E e); // EXTENSION
void notify_deferred(); // EXTENSION
};
template <class Allocator> promise(allocator_arg_t, Allocator a);
构造一个新的 boost::promise
,且不关联任何结果,使用分配器 a
。
无。
只有在定义了 BOOST_THREAD_FUTURE_USES_ALLOCATORS 时才可用。
promise(promise && other);
构造一个新的 boost::promise
,并将与 other
关联的结果的所有权转移到 *this
,使 other
不再关联任何结果。
无。
如果编译器不支持右值引用,则使用 boost.thread 移动模拟来实现。
promise& operator=(promise && other);
将与 other
关联的结果的所有权转移到 *this
,使 other
不再关联任何结果。如果已经有一个与 *this
关联的结果,并且该结果尚未准备就绪,则将与该结果关联的任何 future 设置为准备就绪,并将 boost::broken_promise
异常作为结果。
无。
如果编译器不支持右值引用,则使用 boost.thread 移动模拟来实现。
~promise();
销毁 *this
。如果有一个与 *this
关联的结果,并且该结果尚未准备就绪,则将与该任务关联的任何 future 设置为准备就绪,并将 boost::broken_promise
异常作为结果。
无。
future
<R> get_future();
如果 *this
没有与结果关联,则为新的共享状态分配存储空间并将其与 *this
关联。返回一个与 *this
关联的结果相关联的 future
。
如果与任务关联的 future 已经被检索,则抛出 boost::future_already_retrieved
异常。如果无法分配任何必要的内存,则抛出 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();
- 如果定义了 BOOST_THREAD_PROVIDES_PROMISE_LAZY 并且 *this
没有与结果关联,则为新的共享状态分配存储空间并将其与 *this
关联。
- 将值 r
存储在与 *this
关联的共享状态中。唤醒任何被阻塞并等待异步结果的线程。
所有等待共享状态的 future 都已准备就绪,并且这些 future 的 boost::future<R>::has_value()
或 boost::shared_future<R>::has_value()
应该返回 true
。
- 如果与 *this
关联的结果已经准备就绪,则抛出 boost::promise_already_satisfied
异常。
- 如果 *this
没有共享状态,则抛出 boost::broken_promise
异常。
- 如果无法分配存储结果所需的内存,则抛出 std::bad_alloc
异常。
- 由 R
的复制或移动构造函数抛出的任何异常。
void set_exception(boost::exception_ptr e); template <typename E> void set_exception(E e); // EXTENSION
- 如果定义了 BOOST_THREAD_PROVIDES_PROMISE_LAZY 并且 *this
没有与结果关联,则为新的共享状态分配存储空间并将其与 *this
关联。
- 将异常 e
存储在与 *this
关联的共享状态中。唤醒任何被阻塞并等待异步结果的线程。
所有等待共享状态的 future 都已准备就绪,并且这些 future 的 boost::future<R>::has_exception()
或 boost::shared_future<R>::has_exception()
应该返回 true
。
- 如果与 *this
关联的结果已经准备就绪,则抛出 boost::promise_already_satisfied
异常。
- 如果 *this
没有共享状态,则抛出 boost::broken_promise
异常。
- 如果无法分配存储结果所需的内存,则抛出 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();
将值 r 存储在共享状态中,但不会立即使该状态变为就绪状态。 安排该状态在当前线程退出时变为就绪状态,此时与当前线程关联的所有线程存储持续时间的对象的析构函数都已执行完毕。
与 *this
关联的结果被设置为 deferred (延迟的)。
- 如果与 *this
关联的结果已经是 ready (就绪的)或 deferred (延迟的),则抛出 boost::promise_already_satisfied
异常。
- 如果 *this
没有共享状态,则抛出 boost::broken_promise
异常。
- 如果无法分配存储结果所需的内存,则抛出 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
将异常指针 p 存储在共享状态中,但不会立即使该状态变为就绪状态。 安排该状态在当前线程退出时变为就绪状态,此时与当前线程关联的所有线程存储持续时间的对象的析构函数都已执行完毕。
与 *this
关联的结果被设置为 deferred (延迟的)。
- 如果与 *this
关联的结果已经是 ready (就绪的)或 deferred (延迟的),则抛出 boost::promise_already_satisfied
异常。
- 如果 *this
没有共享状态,则抛出 boost::broken_promise
异常。
- 如果无法分配存储结果所需的内存,则抛出 std::bad_alloc
异常。
template<typename F> void set_wait_callback(F f);
表达式 f(t)
应当格式良好,其中 t
是类型为 boost::promise
的左值。 调用 f
的副本应与调用 f
具有相同的效果。
将 f
的副本存储在与 *this
关联的共享状态中,作为 wait callback (等待回调)。 这将替换与该结果一起存储的任何现有等待回调。 如果线程随后在与此结果关联的 future
或 boost::shared_future
上调用了其中一个 wait 函数,并且结果未 ready (就绪),则应调用 f(*this)
。
如果无法为所需的存储分配内存,则抛出 std::bad_alloc
异常。
void set_value_deferred(R&& r); void set_value_deferred(const R& r); void promise<R&>:: set_value_deferred(R& r); void promise<void>:: set_value_deferred();
- 如果定义了 BOOST_THREAD_PROVIDES_PROMISE_LAZY 并且 *this
没有与结果关联,则为新的共享状态分配存储空间并将其与 *this
关联。
- 将值 r
存储在共享状态中,但不会立即使该状态变为就绪状态。 阻塞等待异步结果的线程不会被唤醒。 它们只有在调用 notify_deferred
时才会被唤醒。
与 *this
关联的结果被设置为 deferred (延迟的)。
- 如果与 *this
关联的结果已经是 ready (就绪的)或 deferred (延迟的),则抛出 boost::promise_already_satisfied
异常。
- 如果 *this
没有共享状态,则抛出 boost::broken_promise
异常。
- 如果无法分配存储结果所需的内存,则抛出 std::bad_alloc
异常。
- 由 R
的复制或移动构造函数抛出的任何异常。
void set_exception_deferred(boost::exception_ptr e); template <typename E> void set_exception_deferred(E e); // EXTENSION
- 如果定义了 BOOST_THREAD_PROVIDES_PROMISE_LAZY 并且 *this
没有与结果关联,则为新的共享状态分配存储空间并将其与 *this
关联。
- 将异常 e
存储在与 *this
关联的共享状态中,但不会立即使该状态变为就绪状态。 阻塞等待异步结果的线程不会被唤醒。 它们只有在调用 notify_deferred
时才会被唤醒。
与 *this
关联的结果被设置为 deferred (延迟的)。
- 如果与 *this
关联的结果已经是 ready (就绪的)或 deferred (延迟的),则抛出 boost::promise_already_satisfied
异常。
- 如果 *this
没有共享状态,则抛出 boost::broken_promise
异常。
- 如果无法分配存储结果所需的内存,则抛出 std::bad_alloc
异常。
任何阻塞等待异步结果的线程都会被唤醒。
所有等待共享状态的 future 都已准备就绪,并且这些 future 的 boost::future<R>::has_value()
或 boost::shared_future<R>::has_value()
应该返回 true
。
与 *this
关联的结果变为 ready (就绪)。
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);
f()
是一个有效的表达式,其返回类型可转换为 R
。 调用 f
的副本必须与调用 f
的行为相同。
构造一个新的 boost::packaged_task
,并将 boost::forward<F>(f)
存储为关联的任务。
- f
的复制(或移动)构造函数抛出的任何异常。
- 如果无法为内部数据结构分配内存,则抛出 std::bad_alloc
异常。
R(*f)(ArgTypes...)) 重载允许传递一个函数,而无需使用 &
。
如果 decay<F>::type 与 boost::packaged_task<R> 的类型相同,则此构造函数不参与重载解析。
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);
f()
是一个有效的表达式,其返回类型可转换为 R
。 调用 f
的副本应与调用 f
的行为相同。
构造一个新的 boost::packaged_task
,并将 boost::forward<F>(f)
存储为使用分配器 a
的关联任务。
f
的复制(或移动)构造函数抛出的任何异常。 如果无法为内部数据结构分配内存,则抛出 std::bad_alloc
异常。
只有在定义了 BOOST_THREAD_FUTURE_USES_ALLOCATORS 时才可用。
R(*f)(ArgTypes...)) 重载允许传递一个函数,而无需使用 &
。
packaged_task(packaged_task && other);
构造一个新的 boost::packaged_task
,并将与 other
关联的任务的所有权转移到 *this
,使 other
没有关联的任务。
无。
如果编译器不支持右值引用,则使用 boost.thread 移动模拟来实现。
packaged_task& operator=(packaged_task && other);
将与 other
关联的任务的所有权转移到 *this
,使 other
没有关联的任务。 如果已经有一个与 *this
关联的任务,并且该任务尚未被调用,则将与该任务关联的任何 future 设置为 ready (就绪),并将 boost::broken_promise
异常作为结果。
无。
如果编译器不支持右值引用,则使用 boost.thread 移动模拟来实现。
~packaged_task();
销毁 *this
。 如果有一个与 *this
关联的任务,并且该任务尚未被调用,则将与该任务关联的任何 future 设置为 ready (就绪),并将 boost::broken_promise
异常作为结果。
无。
future
<R> get_future();
返回一个与 *this
关联的任务结果相关的 future
。
如果与 *this
关联的任务的所有权已转移到 boost::packaged_task
的另一个实例,则抛出 boost::task_moved
异常。 如果与该任务关联的 future 已经被检索,则抛出 boost::future_already_retrieved
异常。
void operator()();
调用与 *this
关联的任务,并将结果存储在相应的 future 中。 如果该任务正常返回,则返回值将作为共享状态存储;否则,将存储抛出的异常。 任何阻塞等待与此任务关联的共享状态的线程都会被唤醒。
所有等待共享状态的 future 都变为 ready (就绪)。
- 如果与 *this
关联的任务的所有权已转移到 boost::packaged_task
的另一个实例,则抛出 boost::task_moved
异常。
- 如果该任务已被调用,则抛出 boost::task_already_started
异常。
void make_ready_at_thread_exit(ArgTypes...);
调用与 *this
关联的任务,并将结果存储到相应的 future 中。如果任务正常返回,则返回值将作为共享状态存储;否则,将存储抛出的异常。无论哪种情况,都不会立即准备好该状态。安排在当前线程退出时,在与当前线程关联的所有线程存储持续时间对象都已销毁后,准备好共享状态。
- 如果与 *this
关联的任务的所有权已转移到 boost::packaged_task
的另一个实例,则抛出 boost::task_moved
异常。
- 如果该任务已被调用,则抛出 boost::task_already_started
异常。
void reset();
重置 packaged_task 的状态,以便可以再次调用它。
如果与 *this
关联的任务的所有权已转移到 boost::packaged_task
的另一个实例,则为 boost::task_moved
。
template<typename F> void set_wait_callback(F f);
表达式 f(t)
应该是形式良好的,其中 t
是类型为 boost::packaged_task
的左值。 调用 f
的副本应与调用 f
具有相同的效果
将 f
的副本与和 *this
关联的任务存储为wait callback。这将替换与该任务一起存储的任何现有 wait callback。如果线程随后在与此任务关联的 future
或 boost::shared_future
上调用 wait 函数之一,并且任务的结果未ready,则应调用 f(*this)
。
如果与 *this
关联的任务的所有权已转移到 boost::packaged_task
的另一个实例,则为 boost::task_moved
。
template <class T> typename decay<T>::type decay_copy(T&& v) { return boost::forward<T>(v); }
函数模板 async 提供了一种机制,可以潜在地在新线程中启动函数,并在 future 对象中提供函数的结果,该 future 对象与其共享一个共享状态。
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);
decay_copy(boost::forward<F>(f))()
应为一个有效的表达式。
第一个函数的行为与使用 launch::async | launch::deferred
的策略参数调用第二个函数,并且 F
的参数相同。
第二个和第三个函数创建一个与返回的 future 对象关联的共享状态。
第二个函数的进一步行为取决于策略参数,如下所示(如果满足多个条件,则实现可以选择任何相应的策略)
- 如果 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
的策略值时,当无法有效利用更多并发时,实现应延迟调用或选择策略。)
- 如果未提供有效的启动策略,则行为未定义。
第三个函数的进一步行为如下
- Executor::submit() 函数会获得一个 function<void ()>,该函数调用 `INVOKE (DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)。执行器的实现由程序员决定。
类型为
的对象,该对象引用对此 future
<typename result_of<typename decay<F>::type()>::type>async
调用创建的共享状态。
无论提供的策略参数如何,
- async
的调用与 f
的调用同步。(注意:即使相应的 future 对象被移动到另一个线程,此语句也适用。); 并且
- 函数 f
的完成在共享状态准备好之前排序。(注意:f
可能根本不会被调用,因此它的完成可能永远不会发生。)
如果实现选择 launch::async
策略,
- 对共享此 async 调用创建的共享状态的异步返回对象调用非定时等待函数将阻塞,直到关联的线程已完成,就像已加入一样,或者超时;
- 关联的线程完成与成功检测共享状态的就绪状态的第一个函数返回,或与释放共享状态的最后一个函数返回同步,以先发生者为准。
如果策略为 launch::async
并且实现无法启动新线程,则为 system_error
。
- resource_unavailable_try_again
- 如果策略为 launch::async
并且系统无法启动新线程。
如果 decay_t<F> is
boost:: launch or
boost::is_executor<F> is
true_type`,则第一个签名不应参与重载解析。
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);
![]() |
警告 |
---|---|
仅在支持右值引用、可变参数模板、decltype 和提供 <tuple> 的标准库(等待可移动感知的 boost::tuple)的 C++11 编译器上提供可变参数原型,并且定义了 BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK。 |
F
和 Args
中的每个 Ti
都应满足 MoveConstructible
要求。
invoke (decay_copy (boost::forward<F>(f)), decay_copy (boost::forward<Args>(args))...)
应为一个有效的表达式。
- 第一个函数的行为与使用 launch::async | launch::deferred
的策略参数调用第二个函数,并且 F
和 Args
的参数相同。
- 第二个函数创建一个与返回的 future 对象关联的共享状态。第二个函数的进一步行为取决于策略参数,如下所示(如果满足多个条件,则实现可以选择任何相应的策略)
- 如果 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))
的评估开始,该函数不再被视为延迟函数。
- 如果未提供有效的启动策略,则行为未定义。
如果此策略与其他策略一起指定,例如在使用 launch::async | launch::deferred
策略值时,如果无法有效地利用更多的并发性,则实现应延迟调用或策略的选择。
类型为
的对象,它引用了对此 future
<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>async
调用的共享状态。
无论提供的策略参数如何,
- async 的调用与 f
的调用同步。(注意:即使相应的 future 对象被移动到另一个线程,此语句也适用。);并且
- 函数 f
的完成先于共享状态变为就绪状态。(注意:f 可能根本没有被调用,因此它的完成可能永远不会发生。)
如果实现选择 launch::async
策略,
- 对异步返回对象(该对象共享由此 async 调用创建的共享状态)的等待函数的调用应阻塞,直到关联的线程完成(如同已加入),或者超时;
- 关联的线程完成与成功检测共享状态的就绪状态的第一个函数返回,或与释放共享状态的最后一个函数返回同步,以先发生者为准。
如果策略为 launch::async
并且实现无法启动新线程,则为 system_error
。
- resource_unavailable_try_again
- 如果策略为 launch::async
并且系统无法启动新线程。
如果 decay<F>::type 是 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
类型 Fn
应是 future
或 boost::shared_future
的特化,并且 Iterator
应是一个前向迭代器,其 value_type
是 future
或 boost::shared_future
的特化。
等待直到至少一个指定的 future 变为就绪状态。
基于范围的重载返回一个 Iterator
,它标识了范围内检测到的第一个处于就绪状态的 future。 剩余的重载返回检测到的第一个处于就绪状态的 future 的基于零的索引(第一个参数 => 0,第二个参数 => 1,依此类推)。
如果当前线程被中断,则抛出 boost::thread_interrupted
。 由与任何正在等待的 future 关联的等待回调引发的任何异常。 如果无法为内部等待结构分配内存,则抛出 std::bad_alloc
。
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
类型 Fn
应是 future
或 boost::shared_future
的特化,并且 Iterator
应是一个前向迭代器,其 value_type
是 future
或 boost::shared_future
的特化。
等待直到所有指定的 future 变为就绪状态。
调用指定 future 上的 wait()
引发的任何异常。
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);
- 对于第一个重载,InputIterator
的值类型应可转换为 future<R>
或 shared_future<R>
。 所有 R
类型必须相同。 如果任何 future<R>
或 shared_future<R>
对象处于无效状态(即 valid() == false
),则行为未定义。 - 对于第二个重载,FutTypes
的类型为 future<R>
或 shared_future<R>
。 在 valid() == false
的 future
或 shared_future
对象上调用 when_all
的效果未定义。
- when_all
有两种变体。 第一个版本接受一对 InputIterators
。 第二个版本接受任意数量的 future<R0>
和 shared_future<R1>
对象,其中 R0
和 R1
不需要是相同的类型。
- 调用 when_all
的第一个签名,当 InputIterator
的 first 等于 last 时,会返回一个具有空 vector
的 future,并且该 future 会立即就绪。
- 调用 when_all
的第二个签名,且不带任何参数时,会返回一个 future<tuple<>>,并且该 future 会立即就绪。
- 如果传递给 when_all
的任何 future 指向尚未开始执行的延迟任务,则这些任务将在调用 when_all
返回之前执行。一旦所有这些任务都已执行,则调用 when_all
会立即返回。
- 调用 when_all
不会等待非延迟任务或已在其他地方开始执行的延迟任务完成才返回。
- 一旦所有传递给 when_all
的 future
s/shared_future
s 就绪,这些 future
s/shared_future
s 就会被移动/复制到从调用 when_all
返回的 future 的关联状态中,并保持传递给 when_all
的 futures 的顺序。
- 然后将集合作为结果存储在一个新创建的共享状态中。
- 创建一个指向该共享状态的新 future 对象。 future 的确切类型将在下面进一步描述。
- 调用 when_all
返回的 future
在调用 wait()
或 get()
时不会抛出异常,但输出集合中保存的 futures 可能会。
- 如果调用 when_all
时不带任何参数,则为 future<tuple<>>
。
- 如果在编译时输入基数未知,并且迭代器对生成 future<R>
,则为 future<vector<future<R>>>
。 输出向量中 futures 的顺序将与输入迭代器给出的顺序相同。
- 如果在编译时输入基数未知,并且迭代器对生成 shared_future<R>
,则为 future<vector<shared_future<R>>>
。 输出向量中 futures 的顺序将与输入迭代器给出的顺序相同。
- 如果输入的数量是固定的,则为 future<tuple<decay_t<FutTypes>...>>
。
- 所有输入 futures valid() == false。
- 所有输入 shared future valid() == true。
- valid() == true。
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);
- 对于第一个重载,InputIterator
的值类型应可转换为 future<R>
或 shared_future<R>
。 所有 R
类型必须相同。 如果任何 future<R>
或 shared_future<R>
对象处于无效状态(即 valid() == false
),则行为未定义。 - 对于第二个重载,FutTypes
的类型为 future<R>
或 shared_future<R>
。 对 valid() == false is undefined
的 future
或 shared_future
对象调用 when_any
的效果是未定义的。
- when_any
有两个变体。 第一个版本采用一对 InputIterators
。 第二个版本采用任意数量的 future<R0>
和 shared_future<R1>
对象,其中 R0
和 R1
不必是相同的类型。
- 调用 when_any
的第一个签名,当 InputIterator
的 first 等于 last 时,会返回一个具有空 vector
的 future,并且该 future 会立即就绪。
- 调用 when_any
的第二个签名,且不带任何参数时,会返回一个 future<tuple<>>,并且该 future 会立即就绪。
- 会按照提供的顺序检查传递给 when_any
的每个 future。 如果给定的 future 已经就绪,则不再检查其他 future,并且对 when_any
的调用会立即返回。 如果给定的 future 指向尚未开始执行的延迟任务,则不再检查其他 future,该任务将被执行,然后对 when_any
的调用会立即返回。
- 调用 when_any
不会等待非延迟任务或已在其他地方开始执行的延迟任务完成才返回。
- 一旦传递给 when_any
的调用的至少一个 future 准备就绪,这些 futures 将被移动到从调用 when_any
返回的 future 的关联状态中,并保持传递给 when_any
的 futures 的顺序。 然后该 future 就绪。
- 然后将集合作为结果存储在一个新创建的共享状态中。
- 创建一个指向该共享状态的新 future 对象。 future 的确切类型将在下面进一步描述。
- 调用 when_any
返回的 future 在调用 wait()
或 get()
时不会抛出异常,但输出集合中保存的 futures 可能会。
- 如果调用 when_any
时不带任何参数,则为 future<tuple<>>
。
- 如果在编译时输入基数未知,并且迭代器对生成 future<R>
,则为 future<vector<future<R>>>
。 输出向量中 futures 的顺序将与输入迭代器给出的顺序相同。
- 如果在编译时输入基数未知,并且迭代器对生成 shared_future<R>
,则为 future<vector<shared_future<R>>>
。 输出向量中 futures 的顺序将与输入迭代器给出的顺序相同。
- 如果输入的数量是固定的,则为 future<tuple<decat_t<FutTypes>...>>
。
- 所有输入 futures valid() == false。
- 所有输入 shared_futures valid() == true。
- 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
其中 V
按如下方式确定: 令 U
为 decay_t<T>
。 那么,如果 U
等于 reference_wrapper<X>
,则 V
为 X&
,否则 V
为 U
。
- value 原型:如果传递给函数的值是一个右值,则该值将被移动到返回的 future 的共享状态。 否则,该值将被复制到返回的 future 的共享状态。
- exception:传递给函数的异常被复制到返回的 future 的共享状态。
.
- 一个具有使用 value
设置的值的就绪 future
- 一个具有使用 ex
设置的异常的就绪 future
- 一个具有设置的值(void)的就绪 future<void>。
- 返回的 future,valid() == true
- 返回的 future,is_ready() = true
- 返回的 future,has_value() = true 或 has_exception() 取决于原型。
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
如果传递给函数的异常或在未给定参数时,当前异常是右值,则该异常将被移动到返回的 exceptional_ptr
。 否则,异常将被复制到返回的 exceptional_ptr
。
一个 exceptional_ptr 实例可以隐式转换为 future<T>
template <typename T> future<typename decay<T>::type> make_future(T&& value); // DEPRECATED future<void> make_future(); // DEPRECATED
如果传递给函数的值是一个右值,则该值将被移动到返回的函数的共享状态。 否则,该值将被复制到返回的函数的共享状态。 .
- future<T>,如果函数给定了一个 T 类型的值
- future<void>,如果函数未给定任何输入。
- 返回的 future<T>,valid() == true
- 返回的 future<T>,is_ready() = true
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
如果传递给函数的值是一个右值,则该值将被移动到返回的函数的共享状态。 否则,该值将被复制到返回的函数的共享状态。 .
- shared_future<T>,如果函数给定了一个 T 类型的值
- shared_future<void>,如果函数未给定任何输入。
- 返回的 shared_future<T>,valid() == true
- 返回的 shared_future<T>,is_ready() = true
make_ready_future()
和 future<>::share()