传递参数为线程函数
如果线程函数包含参数,即使指定参数类型为引用,参数依然会以复制的方式传递过去。考虑函数线程接受一个std::string
参数:
void f(int i, const std::string& str);
std::thread t(f, 3, "hello");
字符串常量值char* const
类型会在新线程中被转换成std:string
类型。而当一个局部变量的指针传递给线程函数时:
void f(int i, const std::string& str);
void oops(int length)
{
char buffer[1024];
sprintf(buffer, "%i", length);
std::thread t(f, 3, buffer);
t.detach();
}
函数oops可能会在参数const std::thread& str
构造完成前退出,悬空指针可能会造成未定义行为,因此需要将其在线程构造时转成std::string
,如下:
std::thread t(f, 3, std::string(buffer));
产生这种risk的根本原因就是就是因为std::thread
的构造函数对传入的参数进行了复制,而不是使用其引用。解决方案是使用std::ref
包装需要被引用的参数,如下:
void update_data(Data& data);
void main()
{
Data data;
std::thread t(update_data, std::ref(data));
t.join();
process(data);
}
std::ref
和std::thread
一样同时在C++11中被引入,主要用于函数式编程。std::thread
的构造函数和std::bind
的机制类似,所以你可以使用一个成员函数作为线程函数,如下:
class Test
{
public:
void do();
}
Test test;
std::thread t(&Test::do, &test);
新线程会调用test.do()
,如果do()
需要接受参数,在std::thread
构造函数添加一个参数即可。
再考虑另一种情况:如果std::thread
接受的参数不能被复制,只能被移动呢?比如其接受一个std::unique_ptr
类型的参数,只有一个std::unique_ptr
的实例可以指向某对象,在多个std::unique_ptr
之间转移对象的使用权会导致原来的拥有者成为空指针。这个时候需要使用std::move
将对象的所有权转移到新的线程中,如下:
void process(std::unique_ptr<Data>);
std::unique_ptr<Data> p_d(new Data);
p_d->prepare_data(42);
std::thread t(process, std::move(p_d));
转移线程所有权
std::thread
支持移动构造,一个线程的所有权可以在不同的std::thread
实例中转移。
void f1();
void f2();
std::thread t1(f1);
std::thread t2 = std::move(t1);
t1 = std::thread(f2);
std::thread t3;
t3 = std::move(t2);
t1 = std::move(t3); //t1已经拥有了一个运行实例,该线程会被std::terminate()
也可以让函数返回一个std::thread
:
std::thread f()
{
void some_function();
return std::thread(some_function);
}
std::thread g()
{
void some_other_function(int);
std::thread tt(some_other_function, 42);
return t;
}
如果要将线程所有权转移到函数中:
void f(std::thread t);
void g()
{
void some_function();
f(std::thread(some_function);
std::thread t(some_function);
f(std::move(t));
}
std::thread
支持移动语义的好处之一,是在上文thread_guard
类的基础上,可以直接获取线程的所有权,而不仅仅是其引用。因此可以构造一个scoped_thread
类,将线程的分离或结合全交给这个类。
class scoped_thread
{
std::thread t;
public:
explicit scoped_thread(std::thread t_):
t(std::move(t_))
{
if(!t.joinable())
throw std::logic_error("Not joinable thread!");
}
~scoped_thread()
{
t.join();
}
scoped_thread(scoped_thread const&) = delete;
scoped_thread& operator=(scoped_thread const&) = delete;
}
struct func;
void f
{
int i;
scoped_thread t(std::thread(func(i)));
...... // do something
}
当程序运行到f
的结尾,scoped_thread
会被析构,而其析构函数不必再check线程是否joinable
,因为在转移所有权时已经检查过。
0 条评论