有件事需要注意,当把函数对象传入到线程构造函数中时,需要避免“最令人头痛的语法解析”(C++’s most vexing parse, 中文简介)。如果你传递了一个临时变量,而不是一个命名的变量;C++编译器会将其解析为函数声明,而不是类型对象的定义。
例如:
std::thread my_thread(background_task());
这里相当与声明了一个名为
my_thread
的函数,这个函数带有一个参数
(
函数指针指向没有参数并返回
background_task
对象的函数
)
,返回一个
std::thread
对象的函数,而非启动了一个线程。
调用join()的行为,还清理了线程相关的存储部分,这样 std::thread 对象将不再与已经完成的线程有任何关联。这意味着,只能对一个线程使用一次join();一旦已经使用过join(), std::thread 对象就不能再次加入了,当对其使用joinable()时,将返回否(false) 。
为了从 std::thread 对象中分离线程(前提是有可进行分离的线程):不能对没有执行线程的 std::thread 对象使用detach(),也是join()的使用条件,并且要用同样的方式进行检查——当 std::thread 对象使用t.joinable()返回的是true,就可以使用t.detach()。
std::thread t(do_background_work);
t.detach();
assert(!t.joinable());
使用多线程方式打开文件:
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
t.detach(); // 2
} else{
process_user_input(cmd);
}
}
}
如果用户选择打开一个新文档,为了让迅速打开文档,需要启动一个新线程去打开新文档
①
,并分离线程
②
。与当前线程做出的操作一样,新线程只不过是打开另一个文件而已。所以,
edit_document
函数可以复用,通过传参的形式打开新的文件。