1 每个程序至少有一个线程;

2 启动线程:

1  std::thread t(function); //定义线程对象,传入参数即启动
2     
3 如果传入的线程函数是一个类的成员函数,则如下
4 A a;//定义一个A的对象a
5 std::thread t(&A::f,a); //将A类的成员函数地址,和A的对象都传进去

3 仿函数:

一个类中,定义了函数调用操作符(),则类对象叫函数对象,也叫仿函数

 1 class A
 2     {
 3     public:
 4     int operate()() //重载函数调用操作符()
 5         {
 6             return 0;
 7         }
 8         int operate()(int x, int y) //重载函数调用操作符()
 9         {
10             return x+y;
11         }
12         int operate()(int x, int y, int z) //函数调用操作符()支持重载
13         {
14             return x+y+z;
15         }
16     }
17     
18     A a; //a即是函数对象,也叫仿函数
19     int ret = a(2,3);

4 线程参数:

线程定义时传入参数给线程的构造函数,既可以传函数名,也可以传仿函数;
std::thread t(hello); //将一个函数名传给线程构造函数
std::thread(a); //将一个无参的函数对象(仿函数)传给线程构造函数
std::thread(a,2,3); //讲一个带参的函数对象,及参数传给线程构造函数
//线程第一个参数必须是函数,所以如果第一个参数是类的对象,则这个类一定定义了函数调用操作符,
//即这个类对象实际上是一个函数对象(仿函数)

5 等待和分离

 1   join();//每个线程只能调用一次join,调用以后必须等该线程执行结束才能往下执行。且该线程对象不再是可连接的,joinable()返回false;
 2     detch();//分离线程。分离后不必等该线程之行结束,程序即可向后执行。
 3             //但detch容易出问题:如果线程中使用了局部变量,函数结束后该线程还在执行,但
 4             struct func{
 5                 int &i;                          //结构体定义了一个引用
 6                 func(int& i_):i(i_){};           //构造函数为引用赋值
 7                 void operate()()
 8                 {
 9                     for(int j=0;j<100000;j++)
10                     {
11                         do_something(i);
12                     }
13                 }
14             }
15             void f()
16             {
17                 int local_mem = 0;
18                 func my_func(local_mem);//仿函数,引用了local_mem
19                 std::thread t(my_func); //启动线程
20                 t.detch();
21             }
22             //上述程序f执行中,t线程分离,f运行结束后,t线程可能未结束,但local_mem已经失效,即线程中使用了未定义的变量,会报错

6 explicit

显示定义为不允许隐式类型转换

 1 class  A
 2     {
 3     public:
 4         int m_x;
 5         A(){};
 6         A(int x){
 7             m_x = x;
 8         };
 9 
10         A( A const &a) = delete;
11         A& operator=(A const &a) = delete;
12     };
13     int main()
14     {
15         A a;
16         a = 1; //相当于1被隐式转换为了A的类型
17                //隐士转换条件:A的构造函数只有一个参数,或者有多个参数,但除了第一个其余都有默认值
18                //若想避免这种隐式转换, 在构造函数钱加上关键字explicit即可
19 20     ///////////////////////////
21     class  A
22     {
23     public:
24         int m_x;
25         A(){};
26         explicit A(int x){
27             m_x = x;
28         };
29     };
30 
31     int main()
32     {
33         A a;
34         //a = 1; //加上explicit后,隐式转换会报错
35         a = (A)1; //只能显示转换
36

7 传递参数给线程函数

1)简单示例:
        void f(int i, std::string const & s);
        std::thread t( f, 3, "hello");
    (2)传参机制
        参数会以默认的方式,被复制到内部存储空间。
        void f( int i, struct_data& w);
        void opp()
        {
            struct_data data;
            std::thread t( f, 1, data);
            t.join();
        }
        //上述opp函数运行后,虽然线程函数f的第二个参数要求是一个结构体数据的引用
        //但是,线程对象的构造函数不会在乎线程函数的诉求,所以是直接将data拷贝一份传入
        //结果就是,线程t内部存贮空间使用的是data副本的引用,而不是data本身的引用
        //所以线程退出后,data的副本随之销毁。对data的修改并没有起作用。
        解决方案:在线程构造时,显示的表示传入的是一个引用即可:
        std::thread t( f, 1, std::ref(data) );

8 转移所有权

 1 线程对象不能复制,但可以转移。可以通过 std::move() 转移一个线程的所有权
 2     std::thread t( f );
 3     std:: thread t1;
 4     t1 = std::move(t); //线程t的所有权将转移到t1
 5     //但不能转移到正在执行的线程中
 6     std::thread t( f );
 7     std:: thread t1;
 8     std:: thread t2;
 9     t1 = std::move(t);
10     t1 = std::move(t2);//线程t1正在执行,此时将t2转移到t1,会调用std::terminate()函数终止程序

9 可利用线程数

标准库提供了方法,可以得到程序运行时能够真正并发运行的线程数量指示:
std::thread::hardware_currency();//返回值可能为0,此时你只要替换你想要的线程数量即可。

10 标识线程

线程标识符是 std::thread::id 类型的
每个std::thread::id 具有两个成员 _Hnd 和 _Id;
线程表示符可以用来复制和比较,输出,
如果相等,则说明是同一个线程;
如不相等,则是不同线程,或其中一个是“没有线程”,即没有相关联的执行线程。
线程标识符提供了完整的比较运算符,可以排序。因此,在容器中可作为主键。

posted on 2021-03-29 16:04  望月又一  阅读(71)  评论(0编辑  收藏  举报