• 【PTHREAD】线程退出与取消


    1 线程退出

    线程正常退出的几种方式:

    • 在线程工作函数中调用pthread_exit,此时可使用pthread_join接收pthread_exit的参数值

    • 线程工作函数运行结束

    • 同一进程中的任意一个子线程调用exit,或者主线程结束

    • 线程可以被从另一个线程中取消pthread_cancel

    1.1 API

    void pthread_exit(void *retval);
    
    • 1
    • 如果为链接态线程,参数retval指示的返回值,可用pthread_join进行接收

    1.2 案例:显式退出链接态线程

    • 源码

      #include 
      #include 
      #include 
      #include 
      
      void *start_routine(void *ptr)
      {
          printf("子线程(%lu)开始运行...\n", pthread_self());
      
          int num = 0;
          while (1)
          {
              printf("子线程(%lu)正在执行,%d...\n", pthread_self(), ++num);
              sleep(1);
              if (num > 3)
                  pthread_exit((void **)"8888");
          }
      
          printf("子线程(%lu)即将退出...\n", pthread_self());
          return (void *)"9999";
      }
      
      int main(int argc, char const *argv[])
      {
          printf("主线程(%lu)开始运行...\n", pthread_self());
      
          pthread_t thread_id;
          pthread_create(&thread_id, NULL, start_routine, NULL);
      
          void *retval = NULL;
          pthread_join(thread_id, &retval);
          printf("子线程的返回值为:%s\n", (const char *)retval);
      
          printf("主线程(%lu)即将退出...\n", pthread_self());
          exit(EXIT_SUCCESS);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
    • 输出

      主线程(140657300412224)开始运行…
      子线程(140657300408064)开始运行…
      子线程(140657300408064)正在执行,1…
      子线程(140657300408064)正在执行,2…
      子线程(140657300408064)正在执行,3…
      子线程(140657300408064)正在执行,4…
      子线程的返回值为:8888
      主线程(140657300412224)即将退出…

    2 线程取消

    2.1 API

    int pthread_cancel(pthread_t thread);
    
    • 1

    发送一个取消请求。目标线程是否响应与什么时候响应依赖于可取消状态可取消类型属性。

    2.2 可取消状态

    int pthread_setcancelstate(int state, int *oldstate);
    
    • 1

    可取消状态可通过函数pthread_setcancelstate函数进行设置。默认为启用。

    • 如果线程禁用(PTHREAD_CANCEL_DISABLE)了可取消状态属性,则取消请求将保留到可取消状态被启用
    • 如果线程启用(PTHREAD_CANCEL_ENABLE)了可取消状态属性,则可取消类型决定取消行为什么时候发生

    2.3 可取消类型

    int pthread_setcanceltype(int type, int *oldtype);
    
    • 1

    可以取消类型可通过函数pthread_setcanceltype函数进行设置。可能值为异步延迟

    • 异步(PTHREAD_CANCEL_ASYNCHRONOUS

      异步的可取消类型意味着可以在任意时刻取消(通常是立即取消,但系统不保证)。

    • 延迟(PTHREAD_CANCEL_DEFERRED

      异步的可取消类型意味着取消被延迟直到下次调用取消点函数

      取消点函数列表可通过man 7 pthreads进行查询

    2.3 取消过程

    void pthread_cleanup_push(void (*routine)(void *), void *arg);
    void pthread_cleanup_pop(int execute);
    
    • 1
    • 2

    当一个取消请求被实施,将按下述步骤进行实施:

    • 取消清理函数入栈与调用(pthread_cleanup_push
    • 线程私有数据的析构函数以不确定顺序的方式被调用
    • 终结线程

    2.4 案例:取消线程执行

    • 源码

      #include 
      #include 
      #include 
      #include 
      
      void *start_routine_01(void *ptr)
      {
          printf("子线程(%lu)开始运行...\n", pthread_self());
      
          int num = 0;
          while (1)
          {
              printf("子线程(%lu)正在执行,%d...\n", pthread_self(), ++num);
              sleep(1);
          }
      
          printf("子线程(%lu)即将退出...\n", pthread_self());
          return (void *)"8888";
      }
      
      void *start_routine_02(void *ptr)
      {
          printf("子线程(%lu)开始运行...\n", pthread_self());
      
          sleep(3);
          pthread_cancel(*((pthread_t*)ptr));
          printf("子线程(%lu)取消信号已发出\n", pthread_self());
      
          printf("子线程(%lu)即将退出...\n", pthread_self());
          return (void *)"9999";
      }
      
      int main(int argc, char const *argv[])
      {
          printf("主线程(%lu)开始运行...\n", pthread_self());
      
          pthread_t thread_id_01;
          pthread_create(&thread_id_01, NULL, start_routine_01, NULL);
      
          pthread_t thread_id_02;
          pthread_create(&thread_id_02, NULL, start_routine_02, &thread_id_01);
      
          pthread_join(thread_id_01, NULL);
          pthread_join(thread_id_02, NULL);
      
          printf("主线程(%lu)即将退出...\n", pthread_self());
          exit(EXIT_SUCCESS);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
    • 输出

      主线程(140162664855360)开始运行…
      子线程(140162664851200)开始运行…
      子线程(140162664851200)正在执行,1…
      子线程(140162656458496)开始运行…
      子线程(140162664851200)正在执行,2…
      子线程(140162664851200)正在执行,3…
      子线程(140162664851200)正在执行,4…
      子线程(140162656458496)取消信号已发出
      子线程(140162656458496)即将退出…
      主线程(140162664855360)即将退出…

  • 相关阅读:
    linux网络-ARP协议
    Android Edittext 实现输入小写字母显示大写字母
    Todolist案例vue写法
    Java注释:类、方法和字段注释
    哈希的应用
    Node.js
    Golang 实现 Redis(10): 本地原子性事务
    openlayer注册4490坐标系,添加4490超图服务
    CF461B Appleman and Tree题解
    十秒钟搞懂linux的软硬链接细节图解和目录结构文件的基本命令
  • 原文地址:https://blog.csdn.net/zhy29563/article/details/126670040