C++11并发编程(一):线程

1、建立线程

thread::join()函数被调用后,调用它的线程会被block,直到线程执行被完成。

以下讲解,主要包括无参、带参、类成员函数作为线程函数。

(1)无参函数

#include <iostream>
#include <thread>

using namespace std;

void func()
{
    cout<<"thread is running"<<endl;
}

int main(int argc, char *argv[])
{
    thread t(func); //创建线程,一旦创建完毕,马上开始运行。
    t.join();

    return 0;
}

结果:

(2)带参函数

当需要向线程函数传递参数时,直接在创建线程时,同时也把参数作为入参传递给线程函数。

注意当调用函数的参数为引用参数时,线程调用需要加上ref关键字表示引用,并且线程函数内会改变引用的变量值。

注意:当使用指针或引用向线程函数传参后,如果指针或引用访问的对象已销毁,会造成线程中访问到非法数据。

#include <iostream>
#include <thread>

using namespace std;

void func1(int n)
{
    n++;
    cout << "n = " << n <<endl;
}

void func2(int &n)
{
    n++;
    cout << "n = " << n <<endl;
}

int main(int argc, char *argv[])
{
    int n = 0;

    thread t1(func1, n);
    t1.join();
    cout << "n = " << n <<endl;

    thread t2(func2, ref(n));
    t2.join();
    cout << "n = " << n <<endl;

    return 0;
}

结果:

(3)类成员函数

类成员函数,需要传递所属对象指针,静态成员函数,不需要。

#include <iostream>
#include <thread>

using namespace std;

class Test
{
public:
    void func1(int n)
    {
        cout << "n = " << n <<endl;
    }
    static void func2(int n)
    {
        cout << "static : n = " << n <<endl;
    }
};

int main(int argc, char *argv[])
{
    Test test;
    thread t1(&Test::func1, &test, 2);
    t1.join();

    thread t2(&Test::func2, 3);
    t2.join();

    return 0;
}

结果:

2、线程操作

thread::join()当thread::join()返回时,线程已经完成,thread对象可以被销毁。

thread::detach(),函数被调用后,执行的线程从线程对象中被分离,已不再被一个线程对象所表达--这是两个独立的事情。C++线程对象可以被销毁,同时OS执行的线程可以继续。如果程序想要知道执行的线程何时结束,就需要一些其它的机制。当主进程结束的时候,detach()出去的子线程不管有没有完成都会被强制杀死。join()函数在那个thread对象上不能再被调用,因为它已经不再和一个执行的线程相关联。

  1. join 会使当前线程阻塞,直到目标线程执行完毕;
    1. 只有处于活动状态线程才能调用join,可以通过joinable()函数检查;
    2. joinable() == true表示当前线程是活动线程,才可以调用join函数;
    3. 默认构造函数创建的对象是joinable() == false;
    4. join只能被调用一次,之后joinable就会变为false,表示线程执行完毕;
    5. 调用 ternimate()的线程必须是 joinable() == false;
    6. 如果线程不调用join()函数,即使执行完毕也是一个活动线程,即joinable() == true,依然可以调用join()函数;
  2. detach 将thread对象及其表示的线程分离;
    1. 调用detach表示thread对象和其表示的线程完全分离;
    2. 分离之后的线程是不在受约束和管制,会单独执行,直到执行完毕释放资源,可以看做是一个daemon线程;
    3. 分离之后thread对象不再表示任何线程;
    4. 分离之后joinable() == false,即使还在执行;

在thread对象析构前,必须要调用join、detach中任意一个。

(1)线程所有权转移

线程管理对象thread与线程,最初由t1管理线程,然后通过move()将t1管理的线程移交给t2,此时线程还是同一个,只是所属权发生改变。

在t1建立时,线程开始执行,在move()移交过程中,其执行不受影响。

由于t1已不再管理线程,故t1不需要调用join或detach,让t1自动析构就行;

t2获得线程管理权,故t2需要调用join等待线程执行结束。

#include <iostream>
#include <thread>

using namespace std;

void func()
{
    cout<<"thread id : " << this_thread::get_id() <<endl;
}

int main(int argc, char *argv[])
{
    thread t1(func);
    thread t2(move(t1)); 

    t2.join();
    return 0;
}

结果:

(2)thread::join()

先等待t1线程执行完毕,然后t2,t3,t4三个线程被调度,交替执行,所以结果比较混乱。

#include <iostream>
#include <thread>

using namespace std;

void func(int n)
{
    cout<<"thread " << n << " is running" <<endl;
}

int main(int argc, char *argv[])
{
    thread t1(func, 1);
    t1.join();

    thread t2(func, 2);
    thread t3(func, 3);
    thread t4(func, 4);
    t2.join();
    t3.join();
    t4.join();
    return 0;
}

结果:

(3)thread::detach()

detach操作可以将线程分离,允许线程独立执行。等到线程执行完毕后,系统会自动将资源回收。

由于线程已脱离t1控制,故t1无需调用join或detach,让t1自动析构就行。

#include <iostream>
#include <thread>

using namespace std;

void func()
{
    cout<<"thread is running" <<endl;
}

int main(int argc, char *argv[])
{
    thread t1(func);
    t1.detach();
    return 0;
}

结果:

3、线程停止

一般情况下当线程函数执行完成后,线程“自然”停止。但在std::thread中有一种情况会造成线程异常终止,那就是:析构

当std::thread实例析构时,如果线程还在运行,则线程会被强行终止掉,这可能会造成资源的泄漏,因此尽量在析构前join一下,以确保线程成功结束。

如果确实想提前让线程结束怎么办呢?一个简单的方法是使用“共享变量”,线程定期地去检测该量,如果需要退出,则停止执行,退出线程函数。

使用“共享变量”需要注意,在多核、多CPU的情况下需要使用“原子”操作。

 

参考链接:

https://www.cnblogs.com/corineru/p/10847394.html

 


若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

本文涉及工程代码,公众号回复:01Thread,即可下载

在这里插入图片描述

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

百里杨

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

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

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

打赏作者

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

抵扣说明:

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

余额充值