二. 线程管控

线程管控

1. 发起线程

​ 当创建线程时,如果传入的是临时变量,而不是具名变量,那么调用构造函数的语法可能与函数声明相同,这就引起输入值二义性问题。此时,编译器会将其解释成函数声明,而不是定义对象。具体如下

class backgroud_task
{
    public:
    void operator()()const
    {
        do_something();
        do_something_else();
    }
};
std::thread my_thread(backgroud_task());//本意是发起新线程,但是却被解释成函数声明:函数名:my_thread,接受了一个参数,返回backgroud_task对象,该指针是对backgroud_task()不当解释,其原意是生成临时的匿名函数对象

​ 解决办法:

//1  实例化一个对象
backgroud_task e;
std::thread my_thread(e);
//2 对右值临时变量增加一个括号
std::thread my_thread((backgroud_task()));
//3 使用大括号进行列表初始化
std::thread my_thread{backgroud_task()};
//4 使用lambda表达式
std::thread my_thread([]{ 
        do_something();
        do_something_else();});

​ 线程一旦启动,我们就需要明确是等待它结束后再执行主程序(与之汇合),还是由它独自运行(与之分离),正常直至进程结束。

join() //等待线程结束后再执行主线程后续操作 (与之汇合)
detach() //将线程与主线程分离,主线线程对子线程不再控制,子线程将一直保持运行 (与之分离)

当传入参数为指针或引用,调用局部变量时,主线程结束,子线程还未结束,此时将会导致线程中指针或引用会访问已被销毁的变量,而导致程序运行崩溃。具体如下:

void function(int& a)
{
    int b=a;
    while(1)
    {
        do_something();
    }
}

void oops()
{
    int s=0;
    std::thread my_thread(function(s));
    my_thread.detach();
}
//此时oops()结束了生命周期,局部变量s也被销毁,但是线程function(),还在引用局部变量s

解决办法:

1.不使用指针或引用,直接进行数据复制 2. 采用join(),等待子线程先结束生命周期

2.出现异常情况下等待

​ 如果线程启动后有异常抛出,而join()尚未执行,则该join()调用就会被略过。为了防止抛异常而导致程序终止,需要在异常中也调用join(),以避免意外生存期问题。具体如下:

void f()
{
    int s=0
    std::thread tt(function(s));
    try
    {
        do_something_in_current_thread();
    }
    catch(...)
    {
        tt.join();//当执行function线程,出现异常,确保join(),函数顺利执行
        throw;
    }
    tt.join();
}

3.向线程函数传递参数

​ 每个线程具有内部存储空间,传递进来的参数会按照默认方式,先复制到此处,新线程才能直接访问他们,然后,这些副本将会被当成临时变量,以右值的形式传给新线程上可用的函数或调用对象,即便是引用也是如此。但是,如果参数是指针,并且指向自动变量,这样就会存在隐患。比如

void fun(int i,std::string const& s);
void oops(int param)
{
    char buffer[1024];//
    sprintf(buffer,"%i",param);
    std::thread tt(fun,3,buffer);//本意:buffer会在新线程内转换成std::string对象,但是在此之前,oops()函数极有可能运行结束,局部变量buffer被销毁
    t.datach();
}

解决办法:在传入之前就完成转换:

void fun(int i,std::string const& s);
void funf(int i,std::string& s);
void oops(int param)
{
    char buffer[1024];//
    sprintf(buffer,"%i",param);
    std::thread tt(fun,3,std::string(buffer));
    //同时,若想传入的是非const的引用,可通过如下
    std::thread tt(funf,3,std::ref(std::string(buffer)));
    t.datach();
}

4.其他

线程归属权移交通过std::move()函数来实现:

void function_1();
void function_2();
std::thread t1(function_1);//创建线程
std::thread t2(std::move(t1));//将t1线程归属权移交给t2
std::thread t1(function_2);//t1创建新的线程

获取线程数量:工具函数:std::thread::hardware_concurrency();返回值表示程序在各次运行中可真正并发的线程数量

获取线程id,可通过std::thread对象调用get_id()和调用std::tis_thread::get_id()获取当前线程id。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力生活的海绵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值