文章目录
- p19 某个线程只可以join()一次
- p22 只有当joinable()返回true时才能调用detach()
- P21 在std::thread对象析构前,必须明确是等待还是分离线程
- P25 移动语义
- P25 将类的成员函数设定为线程函数
- p41 std::mutex和类模板std::lock_guard<>
p19 某个线程只可以join()一次
只要调用了join()
,隶属于该线程的任何存储空间即会因此清除,std::thread
对象遂不再关联到已结束的线程。
事实上,它(std::thread
)与任何线程均无关联。其中的意义是,对于某个给定的线程,join()
仅能调用一次;只要std::thread
对象曾经调用过join()
,线程就不再可汇合(joinable),成员函数joinable()
将返回false。
p22 只有当joinable()返回true时才能调用detach()
若没有与std::thread
对象关联的任何线程,便不能凭空调用detach()
。
只有当t.joinable()
返回true时才可调用t.detach()
。
P21 在std::thread对象析构前,必须明确是等待还是分离线程
在std::thread
对象析构前,必须明确,是等待线程完成(join)还是要与之分离(detach)。
如果等待std::thread
对象销毁之际还没决定好,那std::thread
的析构函数将会调用std::terminate()
终止整个程序。
示例代码:
#include <thread>
#include <iostream>
void do_lengthy_work()
{
std::cerr << __FUNCTION__;
};
int main(void)
{
{
std::thread t(do_lengthy_work);
//t.join()
//理应在这里join或detach
}
while (1)
{
}
}
这里我们既不join也不detach,那么就会导致std::terminate()
被调用,导致程序终止。
P25 移动语义
若源对象是临时变量,移动就会自动发生。
若源对象是具名变量,则必须通过调用std::move()
直接请求转移。
标准库中有一些类的归属权语义是 是只可移动但不可复制的,如std::thread
、std::unique_ptr
。
P25 将类的成员函数设定为线程函数
class X{
public:
void do_lengthy_work();
};
X my_x;
std::thread t(&X::do_lengthy_work, &my_x);
若要将某个类的成员函数设定为线程函数,应传入一个函数指针,指向该成员函数。
此外,还要给出合适的对象指针,作为该函数的第一个参数。
p41 std::mutex和类模板std::lock_guard<>
在C++中,通过构造std::mutex
的实例来创建互斥,调用lock()
成员函数对其加锁,调用unlock()
解锁。
但不推荐直接条约成员函数的做法。
因为,若按此处理,在函数以外的每条代码路径上都要调用unlock()
,包括由于异常退出的路径。(注:这个路径应该指的是代码结束路径,如return、exit,总之,跳出的时候)
推荐用C++标准库提供的类模板std::lock_guard<>
。
示例代码:
#include <algorithm>
#include <list>
#include <mutex>
std::list<int> some_list;
std::mutex some_mutex;
void add_to_list(int new_value)
{
std::lock_guard<std::mutex> guard(some_mutex);
some_list.push_back(new_value);
}
void list_contains(int value_to_find)
{
std::lock_guard<std::mutex> guard(some_mutex);
if(std::find(some_list.begin(), some_list.end(), value_to_find) == some_list.end())
return false;
else
return true;
}
如果使用的是C++17,还可以将
std::lock_guard<>
简化为std::lock_guard
,省略模板参数列表。这是一个新特性,名为类模板参数推导。