• 启动线程,向线程传递参数


    线程执行完入口函数,也会退出,在为一个线程创建一个std::thread对象后,需要等待这个线程结束。
    线程在std::thread对象创建时启动
    构造std::thread对象,std::thread可以用可调用类型来构造: std::thread mythread(f) //会用f的构造函数去初始化mythread,替换默认的构造函数
    std::thread mythread{f} //使用花括号更为保险,能避免语法解析,当传入的是一个临时变量,编译器会将其解析为

    class background_task
    {
    public:
      void operator()() const
      {
        do_something();
        do_something_else();
      }
    };
    
    background_task f;
    std::thread my_thread(f);
    

    函数声明,而不是类型对象的定义
    启动线程后:有两种:①:等待线程结束——加入式 命名对象.join()
    ②:让其自主运行——分离式 命名对象.detach()
    针对线程还没结束,函数以及退出的时候,这时线程还持有函数局部变量的指针或引用;要使线程函数的功能齐全,将数据复制到线程中,而非复制到共享数据中
    调用join():是简单粗暴的等待线程完成,还清理了线程相关的存储部分,这样std::thread对象将不再与已经完成的线程有任何关联,意味着一个线程只能使用一个join()
    如果不想等待线程结束,可以分离线程,但是线程分离,就打破了线程与std::thread对象的联系,不可能有std::therad对象能引用它,分离线程在后台运行不能被加入
    分离线程:没有任何显式的用户接口,在后台运行
    不能对没有执行线程的std::thread对象使用detach(),也是join的使用条件,当std::thread对象使用命名对象.joinable返回true,可以使用命名对象.detach()

    向线程函数传递参数:

              向std::thread构造函数中的可调用对象,或函数传递一个参数很简单,注意:即使参数是引用形式,也可以在新线程中访问
    
    void f(int i, std::string const& s);
    std::thread t(f, 3, "hello");
    
            传参数时,有时候会存在类型的隐式转换,函数有可能在转换成功之前奔溃,需要在构造前,即传参时做类型转换
    
    void f(int i,std::string const& s);
    void not_oops(int some_param)
    {
      char buffer[1024];
      sprintf(buffer,"%i",some_param);
      std::thread t(f,3,std::string(buffer));  // 使用std::string(传参时做的类型转换),避免悬垂指针 ,悬垂指针:指向曾经的对象,但该对象已经不再存在了;  野指针:没有被正确初始化,于是指向一个随机的内存地址
      t.detach();
    }
    

    线程的构造函数盲目的拷贝已提供的变量,构造函数无视原函数期待的函数类型,
    当向线程中的对象传递函数且该函数本身包含引用,需要使用std::ref()将参数转换成引用的形式

    void update_data_for_widget(widget_id w,widget_data& data); // 1
    void oops_again(widget_id w)
    {
      widget_data data;
     std::thread t(update_data_for_widget,w,std::ref(data)); // 2
      display_status();
      t.join();
      process_widget_data(data); // 3
    }
    

    上面讲的都是传函数,下面传自己定义的类型,对象:

    class X
    {
    public:
      void do_lengthy_work();
    };
    X my_x;
    std::thread t(&X::do_lengthy_work,&my_x);
    
    //初始化时,同时传入所需参数
    class X
    {
    public:
      void do_lengthy_work(int);
    };
    X my_x;
    int num(0);
    std::thread t(&X::do_lengthy_work, &my_x, num); //std::thread构造函数的第三个参数是成员函数的第一个参数
    

    上式中提供的参数可以移动,但是不能拷贝

    每个实例(一个命名对象就是一个实例)负责管理一个执行线程,执行线程的所有权可以在多个std::thread实例中互相转移,这时依赖于std::thread实例的可移动且不可复制性
    不可复制性:保证了在同一时间点,一个std::thread只能关联一个执行线程
    可移动性:使程序员自己决定,哪个实例拥有实际执行线程的所有权

    void some_function(); 
    		void some_other_function(); 
    		std::thread t1(some_function); // 1  执行实例化的t1
    		std::thread t2=std::move(t1); // 2  将t1转移到t2 ,t1已经和执行线程没什么关联了
    		t1=std::thread(some_other_function); // 3  移动操作将会隐式调用,因为所有者是一个临时对象
    		std::thread t3; // 4  t3使用默认方式创建,与任何执行线程都没有关联
    		t3=std::move(t2); // 5 将与t2关联线程的所有权转移到t3,因为t2是一个命名对象,需要显示调用std::move
    		t1=std::move(t3); // 6 赋值操作将使程序崩溃,因为t1已经有一个关联的线程,不能通过赋一个新值给std::thread对象的方式来“丢弃一个线程”
    

    当一个线程没有命名对象时,其所有者是个临时对象
    当线程的所有者是个临时对象时,移动操作将会隐式调用
    当线程的所有者是个命名对象时,移动操作要显式调用
    当命名对象有一个关联线程时,不能通过赋值操作给它一个新线程,不允许通过赋一个新值给std::thread对象的方式来丢弃一个线程

  • 相关阅读:
    事件总线Guava EventBus
    DDD—实体和值对象
    DDD—子域和限界上下文
    DDD—什么是领域驱动设计
    DDD—微服务,中台建设为什么需要领域驱动设计
    RabbitMQ 中的 7 种队列模式
    10w 行级别数据的 Excel 导入优化记录
    Java 反射是什么?
    21 条常用 Linux 命令
    一个 java 文件的执行过程
  • 原文地址:https://www.cnblogs.com/ymd12103410/p/9601207.html
Copyright © 2020-2023  润新知