参考书籍:《C++并发编程实战》第一版&&第二版
参考文档:https://zh.cppreference.com/w/cpp/thread
第一章:你好,C++并发世界(略)
第二章:管理线程(2021-05-07 笔记)
类 std::thread 表示单个执行线程,thread 对象允许 move 但不能 copy 。 构造函数:
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
构造 std::thread 实例并将它关联至新创建的执行线程。复制或者移动 func 和 args 的每个元素到内部存储中,并持续在新的执行线程的整个声明周期。在新的线程上执行 invoke。
移动或按值复制线程函数的参数。若需要传递引用参数给线程函数,则必须包装它(例如用 std::ref 或 std::cref )。
忽略来自函数的任何返回值。若函数抛异常,则调用 std::terminate 。为将返回值或异常传递回调用方线程,可使用 std::promise 或 std::async 。
基本操作:
bool joinable() const noexcept;
检查 std::thread 对象是否标识为活跃的执行线程。具体而言,若 get_id()!=std::thread::id() 则返回 true 。故默认构造的 thread 不可 join。
结束执行代码,但仍未 join 的线程仍被当作活跃的执行线程,从而可 join。
调用 join() 或者 detach() 后也为 false 。
std::thread::id get_id() const noexcept;
返回标识与 *this 关联的线程的 std::thread::id 。若无关联的线程,则返回默认构造的 std::thread::id 。
static unsigned int hardware_concurrency() noexcept;
返回实现支持的并发线程数。应该只把该值当做提示。
void join();
阻塞当前线程直至 *this 所标识的线程结束其执行。
void detach();
从 thread 对象分离执行线程,允许执行独立地持续。一旦该线程退出,则释放任何分配的资源。
调用 detach 后 *this 不再占有任何线程。
void swap( std::thread& other ) noexcept;
交换两个 thread 对象的底层 handle 。
练习:
#include
#include
void func1(int a, int& b){
b = a;
}
//守护类,释放时自动join
class scoped_thread
{
public:
explicit scoped_thread(std::thread& t)
: th(std::move(t)){
}
~scoped_thread(){
if (th.joinable())
th.join();
}
scoped_thread(const scoped_thread&) = delete;
scoped_thread& operator = (const scoped_thread&) = delete;
private:
std::thread th;
};
int main()
{
std::thread t1([] { });
if (t1.joinable()) {
//等待线程结束
t1.join();
}
std::thread t2([] { });
if (t2.joinable()) {
//分离不等待,不会在释放时调用std::terminate
t2.detach();
}
//线程函数的参数按值移动或复制。
//如果需要将引用参数传递给线程函数,则必须将其包装(例如,使用std::ref或std::cref)。
int a = 1, b = 2;
std::thread t3(func1, a, std::ref(b));
if (t3.joinable())
t3.join();
//std::thread可移动但不可复制
//std::thread t4(std::move(t3));
scoped_thread scope(t3);
return 0;
}
第三章:在线程间共享数据
(2021-05-16 笔记)
类 std::mutex 是能用于保护共享数据免受从多个线程同时访问的同步原语,即互斥锁、互斥元、互斥体。
std::mutex 基本操作:
void lock();
锁定互斥。若另一线程已锁定互斥,则到 lock 的调用将阻塞执行,直至获得锁。
bool try_lock();
尝试锁定互斥。立即返回。成功则锁定并返回 true ,否则返回 false 。
void unlock();
解锁互斥。互斥必须为当前执行线程所锁定,否则行为未定义。
std::timed_mutex 多两个接口:
template< class Rep, class Period >
bool try_lock_for( const std::chrono::duration& timeout_duration );
尝试锁互斥。阻塞直到经过指定的 timeout_duration 或得到锁,取决于何者先到来。成功获得锁时返回 true , 否则返回 false 。
template< class Clock, class Duration >
bool try_lock_until( const std::chrono::time_point& timeout_time );
尝试锁互斥。阻塞直至抵达指定的 timeout_time 或得到锁,取决于何者先到来。成功获得锁时返回 true ,否则返回 false 。
死锁:两个或多个线程互相持有对方所需要的资源,导致互相等待。
初始化:C++11 开始可用 static 或者 std::call_once 来防范初始化时的多线程竞争。
练习:
#include
#include
#include
#include
#include
#include
#include
//1.自定义多线程操作安全的链表
template
class MyList
{
public:
bool pop(T& value) {
//给需要多线程操作的接口上锁,解决竞争
std::lock_guard guard(mtx);
if (data.size() > 0) {
value = data.front();
data.pop_front();
return true;
}
return false;
}
void push(const T& value) {
std::lock_guard guard(mtx);
data.push_back(value);
}
//同时操作多个mutex时要避免死锁
friend void swap(MyList& left, MyList& right) {
if (&left == &right)
return;
// 用 std::lock 获得二个锁,而不用担心死锁
std::lock(left.mtx, right.mtx);
std::lock_guard lguard(left.mtx, std::adopt_lock);
std::lock_guard rguard(right.mtx, std::adopt_lock);
// 等价代码(若需要 unique_locks ,例如对于条件变量)
//std::unique_lock lk1(left.mtx, std::defer_lock);
//std::unique_lock lk2(right.mtx, std::defer_lock);
//std::lock(lk1, lk2);
// C++17 中可用的较优解法
//std::scoped_lock lk(left.mtx, right.mtx);
std::swap(left.data, right.data);
}
private:
std::list data;
//加mutable是为了在const接口中使用
mutable std::mutex mtx;
};
//2.初始化时保护共享数据
std::once_flag onceflag;
void init() {
}
int* instance() {
static int i;
return &i;
}
//3.递归锁(C++11)
class DemoA {
public:
void func1() {
std::lock_guard lk(m);
//递归锁定了func2中的
func2();
}
void func2() {
std::lock_guard lk(m);
}
private:
std::recursive_mutex m;
};
//4.读写锁(C++17)
class ThreadSafeCounter {
public:
// 多个线程/读者能同时读计数器的值。
unsigned int get() const {
//share共享,只读
std::shared_lock lock(mtx);
return value;
}
// 只有一个线程/写者能增加/写线程的值。
void increment() {
//unique独占,读写
std::unique_lock lock(mtx);
value++;
}
// 只有一个线程/写者能重置/写线程的值。
void reset() {
std::unique_lock lock(mtx);
value = 0;
}
private:
mutable std::shared_mutex mtx;
unsigned int value = 0;
};
//5.时间限定的锁定(C++11)
std::timed_mutex tmtx;
void time_lock() {
//指定时间段,如10ms后超时,或者unlock
//std::chrono::duration
//if (tmtx.try_lock_for(std::chrono::minutes(1))) {
if (tmtx.try_lock_for(std::chrono::duration (1))) {
//...
tmtx.unlock();
}
//指定时间点,如1点钟超时,或者unlock
//std::chrono::time_point
auto t1 = std::chrono::high_resolution_clock::now();
if (tmtx.try_lock_until(t1 + std::chrono::minutes(1))) {
//...
tmtx.unlock();
}
}
int main()
{
//1.线程安全链表
MyList queue;
queue.push(10);
MyList queue2;
queue2.push(20);
swap(queue, queue2);
int value;
if (queue.pop(value)) {
std::cout
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?