C++ 多线程编程(一)- C++11中的线程类

1. C++11 多线程编程库

头文件描述
atomic声明了两个类,std::atomicstd::atomic_flag,还声明了一套原子操作函数
thread声明了 std::thread
mutex提供互斥锁类,包括 std::mutexstd::lock_guardstd::unique_lock
condition_variable提供条件变量类,包括 std::condition_variablestd::condition_variable_any
future提供两个Providerstd::promisestd::package_task,以及两个 Futurestd::futurestd::shared_future,还有一些相关的函数,例如 std::async

2. 线程的创建

线程的创建有3种方式:默认构造函数初始化构造函数移动构造函数

默认构造函数方式和初始化构造函数方式类似,在这里不过多介绍

2.1 初始化构造函数

初始化构造函数其实就是把 线程函数的指针线程函数的参数 一同传入线程类的构造函数中。

#include <iostream>
#include <thread>

using namespace std;

void thread_func_with_arg();
void thread_func_with_one_arg(int arg_1);
void thread_func_with_two_arg(int arg_1, int arg_2);

int main(int argc, char* argv)
{
    cout << "//----------------------  program start  -----------------------//" << endl;
    
    cout << "----> This is the main thread." << endl;

    // 创建线程
    thread thread_0(thread_func_with_arg);              // 无参数线程
    thread thread_1(thread_func_with_one_arg, 1);       // 带一个参数线程
    thread thread_2(thread_func_with_two_arg, 2, 3);    // 带两个参数线程

    // 等待线程结束
    thread_0.join();
    thread_1.join();
    thread_2.join();

    cout << "//----------------------   program end   -----------------------//" << endl;
    
    return EXIT_SUCCESS;
}

void thread_func_with_arg()
{
    cout << "--------> This is a thread without id" << endl;
}

void thread_func_with_one_arg(int arg)
{
    cout << "--------> This is thread with one arg: " << arg << endl;
}

void thread_func_with_two_arg(int arg_1, int arg_2)
{
        cout << "--------> This is thread with two args" << endl
             << "              * arg_1: " << arg_1 << endl
             << "              * arg_2: " << arg_2 << endl; 
}

注意g++编译时,需要加上链接参数 -lpthread.

输出结果为:

//----------------------  program start  -----------------------//
----> This is the main thread.
--------> This is a thread without id
--------> This is thread with two args
              * arg_1: 2
              * arg_2: 3
--------> This is thread with one arg: 1
//----------------------   program end   -----------------------//

在这个例子中,分别创建了3个线程,其中一个线程没有输入参数,一个带有一个输入参数,一个带有两个输入参数。

2.2 移动构造函数

现在不太理解这个移动构造函数,也没看出来这个操作具体有什么优势,这里就不写了以免误导大家,想要深入的话可以参考这个:https://blog.csdn.net/chengqiuming/article/details/89276875

3. 线程的基本操作

3.1 join - 阻塞,等待线程结束

首先我们需要理解:当主线程创建新的子线程后,如果子线程没有被分离(detach),则 当主线程结束的时候,如果子线程还没有结束,则会强制结束子线程

join() 函数的功能是 阻塞 调用对象线程(主线程),并 等待子线程结束 ,子线程结束后,再继续执行主线程剩下的任务。

尝试运行下面的程序:

#include <iostream>
#include <thread>
#include <unistd.h>

using namespace std;

void thread_func();

int main(int argc, char* argv[])
{
    cout << "//----------------------  program start  -----------------------//" << endl;

    thread sub_thread(thread_func);
    // sub_thread.join();   // 1.   有调用 join() 的时候,程序能没有错误地结束
                            // 2.   如果不调用 join(), 则在程序结束时,会提示:
                            //      “terminate called without an active exception”

    for(int i=0; i<5; i++)
    {
        cout << "主线程计数: " << i << endl;
        usleep(500000);
    }

    cout << "//----------------------   program end   -----------------------//" << endl;

    return EXIT_SUCCESS;
}

void thread_func()
{
    cout << "--> 子线程开始" << endl;

    for(int i=0; i<10; i++)
    {
        cout << "子线程计数: " << i << endl;
        usleep(500000);
    }

    cout << "--> 子线程结束" << endl;
    return;
}

如果注释掉 join() 函数,你会得到类似下面的结果:

//----------------------  program start  -----------------------//
主线程计数: 0
--> 子线程开始
子线程计数: 0
主线程计数: 1
子线程计数: 1
主线程计数: 2
子线程计数: 2
主线程计数: 3
子线程计数: 3
主线程计数: 4
子线程计数: 4
//----------------------   program end   -----------------------//
子线程计数: 5
terminate called without an active exception

如果你的结果出现如“主线程计数: 主线程计数: 33”这样的情况,这是因为在打印的时候没有加 ,关于线程的锁会在后面再提及。

这里可以看到,子线程并没有完全执行(计数到9),而是当主线程结束的时候,就被强制结束了。

而如果调用 join() 的话,结果会是:

//----------------------  program start  -----------------------//
--> 子线程开始
子线程计数: 0
子线程计数: 1
子线程计数: 2
子线程计数: 3
子线程计数: 4
子线程计数: 5
子线程计数: 6
子线程计数: 7
子线程计数: 8
子线程计数: 9
--> 子线程结束
主线程计数: 0
主线程计数: 1
主线程计数: 2
主线程计数: 3
主线程计数: 4
//----------------------   program end   -----------------------//

这是因为当调用了 join() 之后,主线程会被阻塞,等待子线程完成,然后 join() 函数才会有返回,join() 函数返回后,主线程方可继续执行它自己的任务。

3.2 detach - 分离线程

上面说到利用 join() 函数,可以阻塞主线程,等待子线程完成以后在继续主线程,从而避免当主线程结束的时候强制结束子线程的问题。

那有没有办法可以既不阻塞主线程,又能保证子线程能够在完成后再结束呢?这时就需要使用 detach() 函数。

detach() 函数可以使子线程从主线程中 分离 出来,当主线程结束的时候,不会再强制结束子线程。

将上面程序的 join() 函数换成 detach()

int main(int argc, char* argv[])
{
	~~~
    thread subThread(thread_func);
    subThread.detach();	
	~~~
}
//----------------------  program start  -----------------------//
主线程计数: 0
子线程计数: 0
主线程计数: 1
子线程计数: 1
子线程计数: 2
主线程计数: 2
子线程计数: 3
主线程计数: 3
子线程计数: 4
主线程计数: 4
子线程计数: 5
//----------------------   program end   -----------------------//

使用了detach() 函数,当主线程结束后,并没有强制结束子线程(没有报“terminate called without an active exception”),这时其实子线程是在后台继续运行的。

哈,暂时没想到有什么简单的方法证明子线程还在运行,先这样吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值