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

    科技2024-01-09  83

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

    1. C++11 多线程编程库2. 线程的创建2.1 初始化构造函数2.2 移动构造函数 3. 线程的基本操作3.1 join - 阻塞,等待线程结束3.2 detach - 分离线程

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

    头文件描述atomic声明了两个类,std::atomic 和 std::atomic_flag,还声明了一套原子操作函数。thread声明了 std::thread 类mutex提供互斥锁类,包括 std::mutex、std::lock_guard 和 std::unique_lockcondition_variable提供条件变量类,包括 std::condition_variable 和 std::condition_variable_anyfuture提供两个Provider类 std::promise 和 std::package_task,以及两个 Future 类 std::future 和 std::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”),这时其实子线程是在后台继续运行的。

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

    Processed: 0.011, SQL: 8