• 线程的使用


    目录

    1,创建线程的几种方式

    2,示例

    3,线程常用方法

    3.1 std::thread类

    3.1.1 成员变量

    3.1.2 thread成员函数

    3.1.2.1 thread 构造函数

    3.1.2.2 thread 析构函数

    3.1.2.3 get_id 获取线程id

    3.1.2.4 joinable 

    3.1.2.5 join 加入

    3.1.2.6 detach 分离

    3.1.2.7 swap 交换线程

    3.1.2.8 join用法示例

    3.1.2.9 detach()用法示例

    3.2 std::this_thread命名空间

    3.2.1 this_thread命名空间的函数介绍

    3.3 pthread函数 (C++98)

    3.3.1 pthread_create 线程创建 

    3.3.2 pthread_join线程挂起

    3.3.3 pthread_exit线程退出

    3.3.4 pthread_self获取当前线程id

    3.3.5 代码示例


    1,创建线程的几种方式

            a> 通过函数指针创建线程

            b> 通过函数对象创建线程

            c> 通过lambda函数创建线程

    使用std::thread类创建对象,必须包含头文件

            #include

    创建的形式是

            std::thread thobj()

            新线程将在创建新对象后立即启动,并将与启动该线程的线程并行执行传递的回调。而且,任何线程都可以通过在该线程的对象上调用join()函数来等待另一个线程退出。

    2,示例

            std::this_thread::sleep_for 函数是休眠函数,表示当前线程休眠一段时间,休眠期间不与其他线程竞争CPU

            std::chrono::seconds 是 std::chrono 库中的一个持续时间类

            std::cref、std::ref 和 std::reference_wrapper
            在 C++ 编程中,有时候我们需要在不进行拷贝的情况下传递引用,或者在需要引用的地方使用常量对象。为了解决这些问题,C++ 标准库提供了三个有用的工具:std::cref、std::ref 和 std::reference_wrapper。
            std::cref 是一个模板函数,用于创建对常量对象的引用。它返回一个 std::reference_wrapper 对象,可以在需要引用的地方使用。这在函数参数传递中特别有用,因为它允许我们在不进行拷贝的情况下传递常量对象,同时保持引用的语义。
          std::ref 是一个模板函数,用于创建对可修改对象的引用。它返回一个 std::reference_wrapper 对象,允许我们在需要引用的地方使用,同时允许修改被引用的对象。
            std::reference_wrapper 是一个模板类,用于包装引用,使其能够在容器中存储或以引用的形式传递。它提供类似引用的语法,并且可以与标准容器一起使用,因为容器无法直接存储引用。

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. class Thread_Test
    6. {
    7. public:
    8. void operator()()
    9. {
    10. for (int i = 0; i < 5; i++)
    11. {
    12. cout << "class Thread_Test is running..." << endl;
    13. std::this_thread::sleep_for(std::chrono::seconds(2));
    14. }
    15. }
    16. void FunctionInner(int m)
    17. {
    18. for (int j = 0; j < m; j++)
    19. {
    20. cout << "class FunctionInner thread is running..." << endl;
    21. std::this_thread::sleep_for(std::chrono::seconds(2));
    22. }
    23. }
    24. };
    25. void thread_func( int m )
    26. {
    27. for (int i = 0; i < m; ++i) {
    28. cout << " thread thread_func is running..." << endl;
    29. std::this_thread::sleep_for(std::chrono::seconds(1));
    30. }
    31. }
    32. int main()
    33. {
    34. // 1.普通函数
    35. int nPara = 3;
    36. std::thread obj_thread1(thread_func, std::cref(nPara));
    37. cout << "thread_func() thread running..." << endl;
    38. obj_thread1.join();
    39. cout << "main enter class thread" << endl;
    40. // 2.仿函数创建
    41. // 方法一
    42. /* thread obj_thread((Thread_Test())); */
    43. // 方法二
    44. Thread_Test c_thread2;
    45. thread obj_thread(c_thread2);
    46. cout << "main thread running..." << endl;
    47. obj_thread.join();
    48. cout << "main enter thread2 running..." << endl;
    49. // 普通成员函数
    50. Thread_Test c_thread3;
    51. int num = 2;
    52. thread obj_thread2(&Thread_Test::FunctionInner, &c_thread3, std::cref(num));
    53. obj_thread2.join();
    54. cout << "lambda thread is entry" << endl;
    55. // 3.lambda 表达式创建
    56. std::thread obj_thread3([]{
    57. for (int i = 0; i < 3; i++)
    58. {
    59. cout << "lambda thread is running..." << endl;
    60. std::this_thread::sleep_for(std::chrono::seconds(3));
    61. }
    62. });
    63. obj_thread3.join();
    64. cout << "main thread is exit" << endl;
    65. return 0;
    66. }

    3,线程常用方法

            头文件包含了std::thread类和std::this_thread命名空间的声明。C++开发中include 头文件,就可以使用std:thread线程类std::this_thread命名空间,std::this_thread这个命名空间包含了对当前线程的一些基本操作,如获取当前线程id、休眠当前线程、让渡当前线程的时间片给其他线程等。

    3.1 std::thread类

            std::thread类来表示执行的各个线程。执行线程是实际上是执行一系列指令,可以在多线程环境中与其他此类序列同时执行,同时共享相同的地址空间。

            一个初始化的线程(std::thread)对象表示活动的执行线程,即初始化后std::thread立即运行;这样的线程对象是可连接的(joinable),并且具有唯一的线程id。默认构造的(未初始化的)线程对象是不可连接的(not joinable),其线程id对于所有不可连接线程都是通用的。

            如果move线程,或者对它们调用join或detach,则可连接线程将变得不可连接。

    3.1.1 成员变量

           id

                    Thread id (public member type)

            native_handle_type

                    Native handle type (public member type)

    示例:

    1. #include <thread>
    2. #include <chrono>
    3. #include <iostream>
    4. using namespace std;
    5. void thread_func(int m)
    6. {
    7. for (int i = 0; i < m; ++i) {
    8. cout << " thread thread_func is running..." << endl;
    9. std::this_thread::sleep_for(std::chrono::seconds(1));
    10. }
    11. }
    12. int main()
    13. {
    14. int nPara = 3;
    15. std::thread obj_thread1(thread_func, std::cref(nPara));
    16. // thread 成员变量 id
    17. std::thread::id f_id = obj_thread1.get_id();
    18. cout << "thread_func() thread running..." << endl;
    19. obj_thread1.join();
    20. cout << "main exit thread" << endl;
    21. return 0;
    22. }

    3.1.2 thread成员函数

            Construct thread // 构造函数
            thread destructor // 析构函数
            operator=// 赋值重载
            get_id // 获取线程id
            joinable// 判断线程是否可以加入等待
            join//Join thread (public member function ) 加入等待
            detach//Detach thread (public member function ) 分离线程
            swap//Swap threads (public member function ) 线程交换
            native_handle// 获取线程句柄
            hardware_concurrency // 检测硬件并发特性
            swap (thread)
            Swap threads (function )

    3.1.2.1 thread 构造函数

            a>  默认构造函数,创建一个空的 thread 执行对象。

            b> 普通构造函数,初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。

            c> 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
            d> move 构造函数(移动分配线程函数),move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。

    拷贝构造函数  (被禁)

    1. // 函数原型
    2. thread(const thread&) = delete;
    3. thread& operator=(const thread&) = delete;
    4. // 错误 示例代码
    5. thread t3(t1);
    6. thread t4 = t1;

    移动分配线程函数

    1. // 代码原型
    2. thread& operator=(thread&& _Other) noexcept
    3. thread(thread&& _Other) noexcept
    4. // 正确的示例代码
    5. thread t5 = std::move(t1);
    6. thread t6(std::move(t1));

     示例:

    1. #include <thread>
    2. #include <chrono>
    3. #include <iostream>
    4. using namespace std;
    5. class Thread_Test
    6. {
    7. public:
    8. void operator()()
    9. {
    10. for (int i = 0; i < 5; i++)
    11. {
    12. cout << "class Thread_Test is running..." << endl;
    13. std::this_thread::sleep_for(std::chrono::seconds(2));
    14. }
    15. }
    16. void FunctionInner(int m)
    17. {
    18. for (int j = 0; j < m; j++)
    19. {
    20. cout << "class FunctionInner thread is running..." << endl;
    21. std::this_thread::sleep_for(std::chrono::seconds(2));
    22. }
    23. }
    24. };
    25. void thread_func(int m)
    26. {
    27. for (int i = 0; i < m; ++i) {
    28. cout << " thread thread_func is running..." << endl;
    29. std::this_thread::sleep_for(std::chrono::seconds(1));
    30. }
    31. }
    32. int main()
    33. {
    34. int nPara = 3;
    35. // a, 默认构造函数
    36. std::thread t1;
    37. // b, 构造函数构造
    38. /* 通过值构造 */
    39. std::thread t2(thread_func, nPara);
    40. /* 通过引用构造 */
    41. std::thread t3(thread_func, std::cref(nPara));
    42. /* 仿函数指针构造 */
    43. std::thread t4((Thread_Test()));
    44. /* 通过仿函数对象构造 */
    45. Thread_Test c_thread2;
    46. std::thread t5(c_thread2);
    47. /* 通过普通成员函数构造 */
    48. Thread_Test c_thread3;
    49. int num = 2;
    50. std::thread t6(&Thread_Test::FunctionInner, &c_thread3, std::cref(num));
    51. // d, move
    52. // t7 is now running . t3 is no longer a thread
    53. std::thread t7(std::move(t3));
    54. t2.join();
    55. t4.join();
    56. t5.join();
    57. t6.join();
    58. t7.join();
    59. cout << "main exit class thread" << endl;
    60. return 0;
    61. }
    3.1.2.2 thread 析构函数

            当线程对象被销毁时,它会自动调用析构函数,如果线程没有被join()或detach(),则程序会终止并抛出std::terminate异常。

    1. // 不调用join()或detach()
    2. // 当my_thread对象离开作用域时会抛出std::terminate异常
    3. std::thread t2(thread_func, nPara);
    3.1.2.3 get_id 获取线程id
     cout << "thread1' id is " << t1.get_id() << endl;
    3.1.2.4 joinable 

    判断线程是否可连接

    bool joinable() const noexcept;

    检查线程是否可连接,返回线程对象是否可连接。

    (1)如果线程对象表示执行线程,则它是可连接的。

    (2)在以下任何情况下,线程对象都不可连接:

                    a, 如果它是默认构造的。

                    b, 如果它已被move(构造另一个线程对象,或分配给另一个线程)。

                    c, 如果已调用其成员join或detach。

    示例:

    1. if (false == t7.joinable())
    2. {
    3. cout << "[error] : t7 is not join! " << endl;
    4. }
    5. else
    6. {
    7. cout << "t7 is join... " << endl;
    8. t7.join();
    9. }
    3.1.2.5 join 加入

            连接线程,阻塞调用线程

                    void join();

            连接(join)线程,当线程执行完成时,函数返回。

            join()函数能够保证调用线程和被调用线程同步,join()函数将阻塞调用该join()函数线程的执行,直到被调用线程的函数返回。调用join()函数后,线程对象变得不可连接,可以安全销毁。

    3.1.2.6 detach 分离

            分离线程,调用线程和被调用线程各自独立运行

                    void detach();

            分离(detach)线程将对象表示的线程与调用线程分离,允许它们彼此独立地执行。

            这两个线程都继续运行,不会以任何方式阻塞或同步。请注意,当其中一方结束执行时,其资源将被释放。调用此函数后,线程对象变得不可连接,可以安全销毁。

    3.1.2.7 swap 交换线程

            void swap (thread& x) noexcept;

            交换线程,将对象的状态与x的状态交换。

            swap函数与thread的移动构造函数和移动赋值函数作用一样。

    示例

    1. cout << "thread1' id is " << t1.get_id() << endl;
    2. cout << "thread2' id is " << t2.get_id() << endl;
    3. cout << "swap after:" << endl;
    4. swap(t1, t2);//线程交换
    5. cout << "thread1' id is " << t1.get_id() << endl;
    6. cout << "thread2' id is " << t2.get_id() << endl;
    3.1.2.8 join用法示例
    1. #include // std::cout
    2. #include // std::thread
    3. void foo()
    4. { // do stuff... }
    5. void bar(int x)
    6. { // do stuff... }
    7. int main()
    8. {
    9. std::thread first (foo);
    10. std::thread second (bar,0);
    11. first.join(); // pauses until first finishes
    12. second.join(); // pauses until second finishes
    13. return 0;
    14. }
    3.1.2.9 detach()用法示例
    1. #include // std::cout
    2. #include // std::thread, std::this_thread::sleep_for
    3. #include // std::chrono::seconds
    4. void pause_thread(int n)
    5. {
    6. std::this_thread::sleep_for (std::chrono::seconds(n));
    7. std::cout << "pause of " << n << " seconds ended\n";
    8. }
    9. int main()
    10. {
    11. std::cout << "Spawning and detaching 3 threads...\n";
    12. std::thread (pause_thread,1).detach();
    13. std::thread (pause_thread,2).detach();
    14. std::thread (pause_thread,3).detach();
    15. std::cout << "Done spawning threads.\n";
    16. std::cout << "(the main thread will now pause for 5 seconds)\n";
    17. // give the detached threads time to finish (but not guaranteed!):
    18. pause_thread(5);
    19. return 0;
    20. }

    输出:

    1. Spawning 5 threads...
    2. Done spawning threads. Now waiting for them to join:
    3. pause of 1 seconds ended
    4. pause of 2 seconds ended
    5. pause of 3 seconds ended
    6. pause of 4 seconds ended
    7. pause of 5 seconds ended
    8. All threads joined!

    3.2 std::this_thread命名空间

            此命名空间提供了访问当前线程的一组函数。

    3.2.1 this_thread命名空间的函数介绍

            get_id     获得当前线程id

            Yield      将当前线程时间片让渡给其他线程

            sleep_until 当前线程休眠直到某个时间点

            sleep_for   当前线程休眠一段时间

    3.3 pthread函数 (C++98)

    3.3.1 pthread_create 线程创建
     

    函数原型

    int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void),void *restrict arg);

      返回值:若是成功建立线程返回0,否则返回错误的编号。
      形式参数:pthread_t *restrict tidp要创建的线程的线程id指针;

                            const pthread_attr_t *restrict attr创建线程时的线程属性;

                            void* (start_rtn)(void)返回值是void类型的指针函数;

                            void *restrict arg start_rtn的形参。

    3.3.2 pthread_join线程挂起

      该函数的作用使得当前线程挂起,等待另一个线程返回才继续执行。也就是说当程序运行到这个地方时,程序会先停止,然后等线程id为thread的这个线程返回,然后程序才会断续执行。
      函数原型:

    int pthread_join( pthread_t thread, void **value_ptr);

    参数说明如下:thread等待退出线程的线程号;

                            value_ptr退出线程的返回值。


    3.3.3 pthread_exit线程退出

            函数原型

    void pthread_exit(void *rval_ptr);

    3.3.4 pthread_self获取当前线程id

            函数原型

    pthread_t pthread_self(void);

    3.3.5 代码示例

    1. #include
    2. #include
    3. #include
    4. #include
    5. void *thread(void *ptr)
    6. {
    7. int i;
    8. for(i=0;i<3;i++){
    9. sleep(1);
    10. printf("This is a pthread.\n");}
    11. }
    12. int main(void)
    13. {
    14. pthread_t id;
    15. int i,ret;
    16. ret=pthread_create(&id,NULL,thread,NULL);
    17. if(ret!=0){
    18. printf ("Create pthread error!\n");
    19. exit (1);
    20. }
    21. for(i=0;i<3;i++){
    22. printf("This is the main process.\n");
    23. sleep(1);
    24. }
    25. pthread_join(id,NULL);
    26. return (0);
    27. }
    28.  
    29. 编译链接命令 g++ -o example2 example.c -lpthread



     

  • 相关阅读:
    Jenkins+Docker+Gitee+SpringBoot自动化部署
    SwiftUI 动态岛开发教程之 07 Live Activities实时活动的要求和限制
    FlinkSQL系列03-表定义
    数据库系统原理与应用教程(045)—— MySQL 查询(七):聚合函数
    Python——字符串
    结构体与共用体
    今日思考(1) — 算力对机器人的影响(基于文心一言的回答)
    jsp数据交互(一)
    LeetCode90:子集②
    CocosCreator使用 ProtoBuf WebSocket与服务器对接方法
  • 原文地址:https://blog.csdn.net/Bossking321/article/details/136369805