C++多线程(一)——线程管理

来源:互联网 发布:云锦东方 知乎 编辑:程序博客网 时间:2024/04/30 00:55

多线程是……/*此处省略一万字,省略的文字详细说明了什么是多线程、其历史及其发展、使用多线程的好处和缺点以及C/C++对多线程的支持的历史*/
C++标准库自C++11标准以来开始支持多线程,多线程相关的类在thread头文件中,所以使用请先必须#include <thread>

启动一个线程

启动一个线程非常简单,例程如下:
#include <iostream>#include <thread>void test1(){std::cout << "------This message from another thread.-------" << std::endl;}int main(){std::thread t(test1);std::cout << "-------This message from main thread.-------" << std::endl;t.join();std::cin.get();return 0;}
上面的代码std::thread t(test1)创建并且启动一个线程,所启动的线程将执行test1()函数中的代码。
线程必须要有一个可调用对象来初始化和启动,这个可调用对象可以是函数对象、一般函数、lambda表达式或者成员函数或者由std::bind()产生的可调用对象。
下面是启动线程的几种不同方式的例程:
#include <iostream>#include <thread>//一般函数void test1(){std::cout << "------This message from another thread.-------" << std::endl;}void test2(int c){std::cout << "parameter is " << c << std::endl;}class callable{public:void operator()(){std::cout << "message from callable object" << std::endl;}void mem(){std::cout << "message from member func." << std::endl;}};int main(){//common functionstd::thread t1(test1);//bindstd::thread t2(std::bind(test2, 10057));callable obj;//callabel objectstd::thread t3(obj);//member functionstd::thread t4(&callable::mem, &obj);//lambda function.std::thread t5([]() {/*nothing to do.*/});std::cout << "-------This message from main thread.-------" << std::endl;t1.join();t2.join();t3.join();t4.join();t5.join();std::cin.get();return 0;}

参数传递

传递进线程中运行的函数也可以有参数,传递参数到线程中也不是什么难题,代码如下:
void f(int i,std::string const& s);std::thread t(f,3,”hello”);
但需要注意的是,所有传递进线程的参数,线程首先将实参值复制到其当前线程空间中。然后使用的是线程空间中的变量。当你的代码如下时,最后的结果可能不是你希望的。
void update_data_for_widget(widget_id w,widget_data& data);void oops_again(widget_id w){widget_data data;std::thread t(update_data_for_widget,w,data);display_status();t.join();process_widget_data(data);}
上述代码首先将data复制到线程空间中,然后再将线程空间中data值的引用传递到update_data_for_widget()函数中,而不是oop_again函数中原data的引用。要想确保传递的是原data的引用,需要用到新的基础设施:std::ref()。就像下面的代码这样:
std::thread t(update_data_for_widget,w,std::ref(data));
这样,你的代码就能正常工作了。

管理一个线程

(1)C++中,线程不可被拷贝构造和赋值构造,唯一能够使用的是移动构造,因此,想要用一个线程去初始化别一个线程,正确的语法如下:
void some_function();void some_other_function();std::thread t1(some_function);std::thread t2=std::move(t1);t1=std::thread(some_other_function);std::thread t3;t3=std::move(t2);t1=std::move(t3);
(2)每一个线程都有一个唯一的标识,其类型为std::thread::id, 线程可以通过std::this_thread::get_id() 函数来获取其id。
(3)细心的读者可以注意到了开始线程的代码片段里用到了线程的成员函数join(),这个函数用于等待线程的结束,否则代码将阻塞在join()函数的调用处。这是因为每个进程都有一个主线程,当主线程结束时,这个进程就会终止,系统最后会回收这个进程的资源。当主线程结束时,其他线程就算没有结束,也会被结束。为了使得其他线程运行结束后进程再终止,由于无法主动获知其他线程会在何时结束,所以必须要主线程中等待其他线程的结束。其他的相关成员函数还有joinable()和detach(),当一个线程detach时,将无法join(等待),而线程是是否可join,可以用joinable()函数来检测。当线程调用detach()函数或者join()函数后,joinable()函数都将返回false。




0 0