Launching a thread
所有callable对象都可以作为thread的参数
void do_some_work();
std::thread my_thread(do_some_work);
class background_task
{
public:
void operator()() const
{
do_something();
do_something_else();
}
};
background_task f;
std::thread my_thread(f);
下面的声明有问题
std::thread my_thread(background_task());
它是一个函数名字为 my_thread
返回值h死 std::thread
,函数的参数,是一个无返回无参数的函数指针.所以最好使用下面的声明形式.
std::thread my_thread((background_task()));
std::thread my_thread{background_task()};
如何声明一个线程变量,而不运行它。也可以使用lambda表达式
std::thread my_thread([]{
do_something();
do_something_else();
});
-
需要注意的,如果你不detach和join线程.那么线程在主线程退出之后,会被terminate掉.
-
如果你detach一个线程,那么即便线程对象销毁后,此线程也可能会跑很长一段时间.thread对象只是C++用来管理计算机线程的对象.如果你detach它了,这个对象自然就可以释放和销毁了.但是计算机的线程却仍然在计算机上运行.
把局部变量带入thread里面了,会发生未定义行为.不要犯错
struct func
{
int& i;
func(int& i_):i(i_){}
void operator()()
{
for(unsigned j=0;j<1000000;++j)
{
do_something(i); // 1. Potential access to dangling
}
}
};
void oops()
{
int some_local_state=0;
func my_func(some_local_state);
std::thread my_thread(my_func);
my_thread.detach(); // 2. Don’t wait for thread to finish
} // 3. New thread might still be running
如果线程参数是一个callable对象,那么它的变量是被拷贝了的。但是如果里面有指针和引用也不能避免,访问被释放资源的区域。所以也应当小心,不要让这样的情况发生.
等待线程结束
你只能调用一次join.无论你是调用thread的join还是detach.在这这时候线程的joinable()都会返回false.你只能操作一次join
or detach.
面对异常:调用join的时机应该斟酌好,因为主线程可能发生异常.你就没有机会去join一个线程或者detach一个线程.
struct func; // <-- See definition in listing 2.1
void f()
{
int some_local_state=0;
func my_func(some_local_state);
std::thread t(my_func);
try
{
do_something_in_current_thread();
}
catch(...)
{
t.join(); // <-- 1
throw;
}
t.join(); // <-- 2
}
这种处理方式不好,用try and
catch的话.很难保证所有的异常都被考虑进去了.所以应该使用RAII方法(Resource
Acquisition Is Initialization)
class thread_guard
{
std::thread& t;
public:
explicit thread_guard(std::thread& t_):
t(t_)
{}
~thread_guard()
{
if(t.joinable()) // <-- 1
{
t.join(); // <-- 2
}
}
thread_guard(thread_guard const&)=delete; // <-- 3
thread_guard& operator=(thread_guard const&)=delete;
};
struct func; // <-- See definition in listing 2.1
void f()
{
int some_local_state=0;
func my_func(some_local_state);
std::thread t(my_func);
thread_guard g(t);
do_something_in_current_thread();
} // <-- 4
=
d
e
l
e
t
e
=delete
=delete
禁止编译器提供默认函数,比如默认复制构造函数和拷贝构造函数。那么对这些对象的复制拷贝会造成编译器错误.
让thread在后台运行
- 线程一旦detach就会被C++ Runtime Library接管,用户不能在通过thread
对象join和控制
std::thread t(do_background_work);
t.detach();
assert(!t.joinable());
如果t.joinable()返回false是不能detach的.
void edit_document(std::string const& filename)
{
open_document_and_display_gui(filename);
while(!done_editing())
{
user_command cmd=get_user_input();
if(cmd.type==open_new_document)
{
std::string const new_name=get_filename_from_user();
std::thread t(edit_document,new_name); //<--1
//(sai:传递参数的一种形式,也可以是使用callable object with member data in it)
t.detach(); //<--2
}
else
{
process_user_input(cmd);
}
}
}
注意
:虽然你detach但是主线程退出了.你的所有线程也会被terminate掉.所以detach的有效性需要得到主线程支持.所谓主线程,就是你run
main函数的那个线程.