本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1、线程所有权的转让
有时候我们需要做这样一个操作,就是将这个线程返回,转换线程的所有权去调用这个函数,这个在C++中就叫做转移线程所有权。我们以一个例子来说明这个问题。
1.1 线程实例之间的转移
void someThing() {
cout << "doing something" << endl;
}
int main() {
std::thread t1(someThing);
thread::id index = t1.get_id();
cout << "转移前的线程ID: "<< index << endl;
std::thread t2 = std::move(t1);
thread::id index2 = t1.get_id();
cout << "转移后的线程ID: " << index2 << endl;
thread::id index3 = t2.get_id();
cout << "转移后新线程ID: " << index3 << endl;
t2.join();
return 0;
}
从上面的日志可以看到,线程ID没有变化,但是已经转移了,通过move的方法 我们把线程t1的占用转移到了线程t2的身上。这样执行完成后,t1就和线程没有关联了。
1.2 函数对象返回 std::thread
void do_someThing() {
cout << "do something" << endl;
};
std::thread f() {
void do_someThing();
return std::thread(do_someThing);
}
int main() {
std::thread t(f());
t.join();
return 0;
}
如上图说是通过函数返回在内部进行所有权转换,这里并没有使用std::move
1.3 函数参数 std::thread
从上面的例子可以看到,线程的所有权可以通过函数返回形式在内部传递,那么可以推出,线程肯定也是可以通过函数的参数进行传递所有权,请看下面的例子
void testThread(std::thread localT) {
localT.join();
}
int main() {
std::thread thread1(someThing);
testThread(std::move(thread1));
return 0;
}
1.4 通过类封装线程
在这里我们通过一个类,来管理,下面我们逐步分析这个封装的思想
void someThing() {
cout << "hello" << endl;
}
struct func {
int& i;
func(int& i_) :i(i_) {
}
void operator()() {
someThing();
//for (unsigned j = 0; j < 1000000; ++j)
//{
// someThing();
//}
}
};
class Scoped_thread {
std::thread t;
public:
explicit Scoped_thread(std::thread t_) :t(std::move(t_)) {
cout << "够造函数" << endl;
if (!t.joinable()) {
throw std::logic_error("No thread");
}
}
explicit Scoped_thread() {
cout << "hel" << endl;
}
/*在释放前调用.join*/
~Scoped_thread() {
cout << "析构函数" << endl;
t.join();
}
Scoped_thread(Scoped_thread const&) = delete;
Scoped_thread& operator=(Scoped_thread const&) = delete;
};
void oops() {
int local_var = 1;
Scoped_thread t(std::move(std::thread(func(local_var))));
cout << "执行完毕" << endl;
}
int main() {
oops();
return 0;
}
1、someThing 作为线程的具体执行的方法
2、struct func 作为一个线程函数
3、Scoped_thread作为封装的主体类 在这个类的构造函数中,我们将外部的线程传递过来,并复制到类中的线程t,在构造函数中判断线程是否存在,通过joinable来实现
4、在析构函数中调用join()
5、Scoped_thread t(std::move(std::thread(func(local_var))));在外部使用move转移线程的所有权给内部线程
1.5 批量线程的处理
在很多场景,我们经常需要创建很多线程,然后使用这些线程完成一个任务,等这些线程全部结束的时候,整个任务也算是结束了
void do_somthing3(unsigned int d) {
cout << "task" << endl;
}
void func() {
std::vector<std::thread> threads;
for (unsigned i = 0; i < 20; ++i) {
threads.push_back(std::thread(do_somthing3, i));
}
// mem_fn会根据成员函数指针推断可调用函数的类型,就省去了另外指定的步骤。
std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
}
int main() {
func();
return 0;
}
在这里我们可以看到,我们通过STL中的 vector装了20个线程,然后通过for_each遍历,并且调用join()