第2章 线程管理
本章内容
学习通过 std::thread 对象管理线程
thread源码thread创建与销毁- 参数传递
join和detach- 线程转移
1. std::thread 源码
thread 结构:
数据成员
-
thrid: 线程 idhandle:win32句柄
函数成员
-
构造
- 默认构造
- 线程函数与参数构造
-
支持移动操作
-
删除拷贝操作
-
joinable,join和detach -
swap,get_id和hardware_concurrency
class thread
{
private:
struct identifier
{
void* handle;
unsigned int id;
} thr;
public:
thread() : thr{} {}
template<class Fn, class... Args>
explicit thread(Fn&& func, Args&&... args)
{
_Start(std::forward<Fn>(func), std::forward<Args>(args)...);
}
~thread()
{
if (joinable())
{
std::terminate();
}
}
thread(thread&& other) : thr(std::exchange(other.thr, {})) {}
thread& operator=(thread&& other)
{
if (joinable())
{
std::terminate();
}
thr = std::exchange(other.thr, {});
return *this;
}
thread(const thread&) = delete;
thread& operator=(const thread&) = delete;
bool joinable() const
{
return thr.id != 0;
}
void join()
{
if (!joinable()) // 1. 是否 joinable
{
_Throw_Cpp_error(_INVALID_ARGUMENT);
}
if (thr.id == _Thrd_id()) // 2. 是否是当前线程
{
_Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR); // 资源死锁
}
if (_Thrd_join(thr, nullptr) != _Thrd_success) // 3. 是否执行成功
{
_Throw_Cpp_error(_NO_SUCH_PROCESS); // 没有该线程
}
thr = {};
}
void detach()
{
if (!joinable())
{
_Throw_Cpp_error(_INVALID_ARGUMENT);
}
_Check_C_return(_Thrd_detach(thr));
thr = {};
}
void swap(thread& other)
{
std::swap(thr, other.thr);
}
unsigned int get_id() const
{
return thr.id;
}
static unsigned int hardware_concurrency();
};
2. thread 创建与销毁
构造
默认构造
thr{} 使得内置类型 thr.handle 和 thr.id 都被初始化为 0
thread() : thr{} {}
函数构造
func 是线程要执行的函数, args 是该函数的参数
模板中的 && 为万能引用
std::forward 用于完美转发
万能引用 + 完美转发 = 将参数以原本的属性向下传递
template<class Fn, class... Args>
explicit thread(Fn&& func, Args&&... args)
{
_Start(std::forward<Fn>(func), std::forward<Args>(args)...);
}
参数传递
thread 构造函数按值传递方式传递线程函数的参数
- 实参先复制到线程栈空间, 再调用线程函数
void fun(int, char&) {}
char c = 'a';
thread t(fun, 1, c); // 编译不通过
thread t(fun, 1, std::ref(c)) // 编译通过
类成员函数
传递对象指针
class A
{
public:
void fun() {}
};
A a;
std::thread t(&A::fun, &a);
析构
析构时必须 非 joinable
等价说法:
- 调用过
join或detach - 线程 id 为 0
~thread()
{
if (joinable())
{
std::terminate();
}
}
3. thread 操作
joinable
查看是否还管理着线程
等价说法:
- 线程是否能汇合
- 线程 id 是否不为 0
bool joinable() const
{
return thr.id != 0;
}
join
A 执行 B.join()
B 阻塞 A, 直至 B 执行完
void join()
{
if (!joinable()) // B.id == 0
{
_Throw_Cpp_error(_INVALID_ARGUMENT);
}
if (thr.id == _Thrd_id()) // B.id == A.id
{
_Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR);
}
if (_Thrd_join(thr, nullptr) != _Thrd_success) // 是否执行成功
{
_Throw_Cpp_error(_NO_SUCH_PROCESS);
}
thr = {}; // B.id = B.handle = 0
}
问题:
-
A 执行 A.join() 会死锁
A { A.join(); // A 阻塞 A } -
A 执行 B.join(), B 执行 A.join() 会死锁
A { B.join(); // B 阻塞 A } B { A.join(); // A 阻塞 B }
detach
将线程交给 C++ 运行时库管理
void detach()
{
if (!joinable())
{
_Throw_Cpp_error(_INVALID_ARGUMENT);
}
_Check_C_return(_Thrd_detach(thr));
thr = {};
}
4. thread 支持移动操作
thread 支持移动构造和移动赋值
thread(thread&& other) : thr(std::exchange(other.thr, {})) {}
thread& operator=(thread&& other)
{
if (joinable())
{
std::terminate();
}
thr = std::exchange(other.thr, {});
return *this;
}
例:
void fun();
thread t1 = thread(fun);
thread t2 = move(t1);
t2 = thread(fun); // 出错: t2.id 必须为 0
5. RAII 封装 thread
问题: thread 构造时满足 RAII 思想, 但析构不满足
class j_thread
{
std::thread t;
public:
...
// 析构函数 RAII 释放资源
~j_thread()
{
if(t.joinable())
t.join();
}
}
总结
-
thread 数据成员: 句柄和线程 id
-
thread 函数成员
- 构造析构
- 支持移动操作
- 删除拷贝操作
joinable,join和detachswap,get_id和hardware_concurrency
- thread 构造函数将线程函数的参数按值传递方式传递
- thread 不满足 RAII
- joinable 判断线程 id 是否不为 0
- join 阻塞, detach 非阻塞