1.std::thread
thread& operator=( thread&& other ) noexcept
thread对象创建就不讲了,但是给已经创建的thread对象赋值需要注意:
1)如果左值是运行中进程(可joinable()==true),则在进行move 赋值时会调用terminate接收进程并抛异常。
2)如果左值运行的进程以及detach了,则不会call terminate;
3)如果左值是空thread 对象,则直接move赋值。
2. 互斥变量
std::mutex,最基本的 Mutex 类。
std::recursive_mutex,递归 Mutex 类。
std::timed_mutex,定时 Mutex 类。
std::recursive_timed_mutex,定时递归 Mutex 类
std::mutex 既不可复制亦不可移动。mutex 提供排他性非递归所有权语义:
bool try_lock();尝试锁定互斥。立即返回。成功获得锁时返回 true ,否则返回 false
std::recursive_mutex,递归 Mutex 类。排他性递归所有权语义.在同一个线程内对各个函数上锁解锁分而治之。
从它成功调用 lock 或 try_lock 开始的时期里占有 recursive_mutex 。此时期间,线程可以进行对 lock 或 try_lock 的附加调用。所有权的时期在线程调用 unlock 匹配次数时结束。
线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调用 lock )或收到 false 返回值(对于调用 try_lock )。
可锁定 recursive_mutex 次数的最大值是未指定的,但抵达该数后,对 lock 的调用将抛出 std::system_error 而对 try_lock 的调用将返回 false 。
std::timed_mutex 类是能用于保护数据免受多个线程同时访问的同步原语。timed_mutex 提供排他性非递归所有权语义。
try_lock_for 函数在 “一段时间范围之内” 线程如果没有获得锁则被阻塞住,直到在此期间内其他线程释放了锁,则该线程可以获得对互斥量的锁返回,或在指定时间内还是没有获得锁, 超时返回 false。
using Ms = std::chrono::milliseconds;
std::this_thread::sleep_for(Ms(100)); //mtx.try_lock_for(std::chrono::milliseconds(200))
try_lock_until 函数则接受 “一个时间点” 作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁返回,如果到达指定时间点没有获得锁,则返回 false。
auto now=std::chrono::steady_clock::now();
test_mutex.try_lock_until(now + std::chrono::seconds(10));
3. 互斥封装器
std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。不可移动不可复制。
————互斥封装器,创建后在作用域块期间占有互斥, 离开创建lock_guard 对象的作用域时自动销毁。
std::unique_lock 方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。可移动不可复制。
通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。
若 Mutex 满足可锁 (Lockable) 要求,则 unique_lock 亦满足可锁 (Lockable) 要求(例如:能用于 std::lock ) ;
若 Mutex 满足定时可锁 (TimedLockable) 要求,则 unique_lock 亦满足定时可锁 (TimedLockable) 要求。
std::unique_lock重载了lock和unlock方法,它可以作为std::lock的参数;而std::lock_guard不行。
std::unique_lock的实例不依赖于std::mutex等锁,所以在初始化的时候,他有空构造函数的,
这就导致了std::unique_lock的锁有两种状态,即内部有无锁,这个可以通过bool std::unique_lock.owns_lock()来检测;
explicit operator bool() const noexcept; (C++11 起) 检查 *this 是否占有互斥。等效地调用 owns_lock()
release 将关联互斥解关联,而不解锁它。返回之前关联的互斥,如果关联互斥为空则返回nullptr
_Mutex *release()_ noexcept
{
//disconnect
_Mutex * _Res = _Pmtx;
_Pmtx = nullptr;
_Owns = false
return (_Res);
}
包装器对象在operator= 赋值时,如果已经lock关联互斥,则会先解锁,再替换unique_lock对象。
1) explicit unique_lock( mutex_type& m );默认构造是通过调用 m.lock() 锁定关联互斥。
2)构造使用try_to_lock_t 尝试锁定关联互斥而不阻塞 (作用是告诉构造函数,请尝试获取锁的所有权;)
3)std::defer_lock,作用是告诉构造函数,不锁定关联互斥,将会在后续的执行中进行获取。
这里的一个应用是用std::lock给std::unique_lock上锁
std::unique_lock<std::mutex> lk_b(m_b, std::defer_lock);
std::unique_lock<std::mutex> lk_c(m_c, std::defer_lock);
std::lock(lk_b, lk_c);
4)adopt_lock_t 调用方线程已拥有互斥的所有权(告诉构造函数,在构造之前,该锁的所有权就已经被获取,请不要尝试获取)
std::lock(from.m, to.m);
// 保证二个已锁定互斥在作用域结尾解锁
std::lock_guard<std::mutex> lock1(from.m, std::adopt_lock);
std::lock_guard<std::mutex> lock2(to.m, std::adopt_lock);
4. promise - future 结构
std::promise 不可复制,可move。
void print_int(std::future<int>& fut) {
printf("==\n");fflush(stdout);
int x = fut.get(); // 阻塞等待,获取共享状态的值.
std::cout << "value: " << x << '\n'; // 打印 value: 10.
}
//promise 是 promise-future 交流通道的“推”端
int main(){
std::promise<int> prom; // 生成一个 std::promise<int> 对象.
std::future<int> fut = prom.get_future(); // 和 future 关联.
std::thread t(print_int, std::ref(fut)); // 将 future 交给另外一个线程t.
sleep(3);
prom.set_value(10); // 设置共享状态的值, 此处和线程t保持同步. 存储值于共享状态的操作与等待函数fut.get()同步
t.join();
return 0;
}
std::future<int> accumulate_future = accumulate_promise.get_future();
//accumulate_future.wait(); // 等待结果
//get 方法等待直至 future 拥有合法结果并(依赖于使用哪个模板)获取它。它等效地调用 wait() 等待结果。
printf("do wait\n");fflush(stdout);
std::cout << "result=" << accumulate_future.get() << '\n';fflush(stdout);
printf("do wait over\n");fflush(stdout);
std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。