当前位置: 首页 > news >正文

【PTHREAD】线程创建

1 API

int pthread_create(pthread_t *thread,
                   const pthread_attr_t *attr,
                   void *(*start_routine) (void *),
                   void *arg);
  • 参数
    • thread
      线程的唯一标识。在线程内部使用pthread_self()获取。
    • attr
      线程属性。设置为NULL表示使用默认属性。
    • start_routime
      线程工作函数。类型为以void *为参数,并返回void *
    • arg
      传递给线程工作函数(start_routime)的数据指针。设置为NULL标识无需传递数据。

2 案例:默认值创建线程

  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    void *start_routine(void *ptr)
    {
        printf("子线程(%lu)等待3秒后退出...\n", pthread_self());
        sleep(3);
        printf("子线程(%lu)即将退出...\n", pthread_self());
        return (void *)NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        printf("主线程(%lu)开始运行...\n", pthread_self());
        pthread_t thread_id;
    
        {
            // 以默认线程属性,创建无数据传递的线程
            pthread_attr_t attr;
            pthread_attr_init(&attr);
            pthread_create(&thread_id, &attr, start_routine, NULL);
            pthread_attr_destroy(&attr);
    
            // 下面这条语句与上面的四条语句等价
            // pthread_create(&thread_id, NULL, start_routine, NULL);
        }
    
        printf("主线程(%lu)等待5秒后结束...\n", pthread_self());
        sleep(5);
    
        printf("主线程(%lu)即将退出...\n", pthread_self());
        exit(EXIT_SUCCESS);
    }
    
  • 输出

    主线程(139943698245440)开始运行…
    主线程(139943698245440)等待5秒后结束…
    子线程(139943698241280)等待3秒后退出…
    子线程(139943698241280)即将退出…
    主线程(139943698245440)即将退出…

3 案例:线程传参

  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.h>
    
    typedef struct _student
    {
        int id;
        int age;
        char name[32];
    } student;
    
    void *start_routine(void *ptr)
    {
        printf("子线程(%lu)等待3秒后退出...\n", pthread_self());
        sleep(3);
        student *pstu = (student *)ptr;
        printf("子线程(%lu)接收的数据: name = %s, age = %d, id = %d\n",
               pthread_self(), pstu->name, pstu->age, pstu->id);
        printf("子线程(%lu)即将退出...\n", pthread_self());
        return (void *)NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        printf("主线程(%lu)开始运行...\n", pthread_self());
    
        student stu;
        bzero(&stu, sizeof(stu));
        strcpy(stu.name, "zhang san");
        stu.age = 18;
        stu.id = 2022;
    
        pthread_t thread_id;
        {
            printf("父线程(%lu)传递的数据: name = %s, age = %d, id = %d\n",
                   pthread_self(), stu.name, stu.age, stu.id);
            pthread_create(&thread_id, NULL, start_routine, &stu);
        }
    
        printf("主线程(%lu)等待5秒后结束...\n", pthread_self());
        sleep(5);
    
        printf("主线程(%lu)即将退出...\n", pthread_self());
        exit(EXIT_SUCCESS);
    }
    
  • 输出

    主线程(139644495886144)开始运行…
    父线程(139644495886144)传递的数据: name = zhang san, age = 18, id = 2022
    主线程(139644495886144)等待5秒后结束…
    子线程(139644495881984)等待3秒后退出…
    子线程(139644495881984)接收的数据: name = zhang san, age = 18, id = 2022
    子线程(139644495881984)即将退出…
    主线程(139644495886144)即将退出…

4 线程属性之CPU时钟

#include <pthread.h>
#include <time.h>

int pthread_getcpuclockid(pthread_t thread, clockid_t *clock_id);

5 线程属性之调度策略与调度参数

int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);
int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param);

6 线程属性之调度优先级

int pthread_setschedprio(pthread_t thread, int prio);

7 案例:获取线程属性

  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.h>
    #include <time.h>
    
    void *start_routine(void *ptr)
    {
        printf("子线程(%lu)等待3秒后退出...\n", pthread_self());
    
        clockid_t clock_id;
        pthread_getcpuclockid(pthread_self(), &clock_id);
        printf("子线程(%lu)的CPU时钟ID(%d)\n", pthread_self(), clock_id);
    
        int sched_policy;
        struct sched_param param;
        pthread_getschedparam(pthread_self(), &sched_policy, &param);
        printf("子线程(%lu)的调度策略(%s)\n", pthread_self(),
               sched_policy == SCHED_FIFO ? "SCHED_FIFO" :
               sched_policy == SCHED_RR  ? "SCHED_RR" :
               sched_policy == SCHED_OTHER ? "SCHED_OTHER" : "???");
        printf("子线程(%lu)的调度优先级(%d)\n", pthread_self(), param.sched_priority);
    
        printf("子线程(%lu)即将退出...\n", pthread_self());
        return (void *)NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        printf("主线程(%lu)开始运行...\n", pthread_self());
    
        pthread_t thread_id;
        pthread_create(&thread_id, NULL, start_routine, NULL);
    
        clockid_t clock_id;
        pthread_getcpuclockid(pthread_self(), &clock_id);
        printf("主线程(%lu)的CPU时钟ID(%d)\n", pthread_self(), clock_id);
    
        int sched_policy;
        struct sched_param param;
        pthread_getschedparam(pthread_self(), &sched_policy, &param);
        printf("主线程(%lu)的调度策略(%s)\n", pthread_self(),
               sched_policy == SCHED_FIFO ? "SCHED_FIFO" :
               sched_policy == SCHED_RR  ? "SCHED_RR" :
               sched_policy == SCHED_OTHER ? "SCHED_OTHER" : "???");
        printf("主线程(%lu)的调度优先级(%d)\n", pthread_self(), param.sched_priority);
    
        printf("主线程(%lu)等待5秒后结束...\n", pthread_self());
        sleep(5);
    
        printf("主线程(%lu)即将退出...\n", pthread_self());
        exit(EXIT_SUCCESS);
    }
    
  • 输出

    主线程(139759329433408)开始运行…
    主线程(139759329433408)的CPU时钟ID(-50154)
    子线程(139759329429248)等待3秒后退出…
    子线程(139759329429248)的CPU时钟ID(-50162)
    子线程(139759329429248)的调度策略(SCHED_OTHER)
    子线程(139759329429248)的调度优先级(0)
    子线程(139759329429248)即将退出…
    主线程(139759329433408)的调度策略(SCHED_OTHER)
    主线程(139759329433408)的调度优先级(0)
    主线程(139759329433408)等待5秒后结束…
    主线程(139759329433408)即将退出…

8 线程属性之私有数据

程序经常需要在不同的线程中使用全局或静态变量,这些变量不同的线程中拥有不同的值。由于同一个进程中的所有线程共享相同的内存空间,因此常规的变量无法实现该需求。

为满足以上需求,提出了线程私有数据的概念。每个线程有一个私有的内存块,这个内存块就是线程的特有区域。该区域使用键值进行索引,使用void *类型。

当创建一个线程时,所有的线程私有数据的键值均为NULL

typedef unsigned int pthread_key_t;

int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));
int pthread_key_delete(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *pointer);
void * pthread_getspecific(pthread_key_t key);
  • pthread_key_create

    • 用于创建一个新的键值。

    • 参数destr_function如果非NULL,指定与键值相关的析构函数。当线程通过pthread_exit或取消操作进行终结时,指定的析构函数被调用,其参数为键值对应的数据。如果赋值为NULL,则不进行调用。如果有多个键值对应的数据需要析构,则其调用的顺序是不确定的。

2.2.9 案例:私有数据的使用

  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.h>
    
    pthread_key_t key;
    
    void *start_routine(void *ptr)
    {
        printf("子线程(%lu)开始运行...\n", pthread_self());
        pthread_key_t key = *((pthread_key_t *)ptr);
    
        // 主线程向KEY中写入值
        int specific_data = 88;
        pthread_setspecific(key, &specific_data);
    
        sleep(3);
    
        // 主线程从KEY中获取值
        int *p_specific_data = pthread_getspecific(key);
        printf("主线程(%lu)的私有数据(%d), 地址(%p)\n", pthread_self(), *p_specific_data, p_specific_data);
    
        printf("子线程(%lu)即将退出...\n", pthread_self());
        return (void *)NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        printf("主线程(%lu)开始运行...\n", pthread_self());
    
        // 初始化KEY
        pthread_key_create(&key, NULL);
    
        // 主线程向KEY中写入值
        int specific_data = 99;
        pthread_setspecific(key, &specific_data);
    
        pthread_t thread_id;
        pthread_create(&thread_id, NULL, start_routine, &key);
    
        // 主线程从KEY中获取值
        int *p_specific_data = pthread_getspecific(key);
        printf("主线程(%lu)的私有数据(%d), 地址(%p)\n", pthread_self(), *p_specific_data, p_specific_data);
    
        sleep(5);
    
        // 销毁KEY
        pthread_key_delete(key);
    
        printf("主线程(%lu)即将退出...\n", pthread_self());
        exit(EXIT_SUCCESS);
    }
    
  • 输出

    主线程(140110147004224)开始运行…
    主线程(140110147004224)的私有数据(99), 地址(0x7ffe404081f4)
    子线程(140110147000064)开始运行…
    主线程(140110147000064)的私有数据(88), 地址(0x7f6def8a8ec8)
    子线程(140110147000064)即将退出…
    主线程(140110147004224)即将退出…

10 判断线程ID是否相等

int pthread_equal(pthread_t t1, pthread_t t2);
  • 相等返回非零
  • 不等但会零

11 单次初始化

pthread_once_t once_control = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
  • 用于保证参数init_routine中的代码最多执行一次

13 案例:单次初始化的使用

  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <time.h>
    
    pthread_once_t once = PTHREAD_ONCE_INIT;
    
    void init_routine(void)
    {
        printf("线程(%lu)执行初始化\n", pthread_self());
    }
    
    
    void *start_routine(void *ptr)
    {
        printf("子线程(%lu)开始运行...\n", pthread_self());
        pthread_once(&once, init_routine);
        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, NULL);
    
        pthread_t thread_id_02;
        pthread_create(&thread_id_02, NULL, start_routine, NULL);
    
        printf("主线程(%lu)即将退出...\n", pthread_self());
        exit(EXIT_SUCCESS);
    }
    
  • 输出

    主线程(140486961612608)开始运行…
    子线程(140486961608448)开始运行…
    线程(140486961608448)执行初始化
    子线程(140486961608448)即将退出…
    主线程(140486961612608)即将退出…

相关文章:

  • s19.基于 Kubernetes v1.25.0(kubeadm) 和 Docker 部署高可用集群(一)
  • 力扣记录:Hot100(4)——75-101
  • 数据结构之——OJ题环形队列实现详解
  • 基于樽海鞘群算法的线性规划求解matlab程序
  • qt自定义控件之TextEdit
  • 深度估计 双目深度估计+单目深度估计 ONNX运行程序
  • Express 路由
  • 2022蓝帽杯初赛电子取证
  • 数据结构与算法复习:第三十六弹
  • CSS - 响应式布局(一)媒体查询
  • 【JAVA预备】课程目录
  • 2022年0902Maven的依赖学习<第四课>
  • Android 11 上的文件读写权限(MANAGE_EXTERNAL_STORAGE)
  • Vue模板语法(01)
  • 世界第一台通用计算机:ENIAC
  • 08.Android之View事件问题
  • 77. Combinations
  • Android Studio:GIT提交项目到远程仓库
  • css属性的继承、初识值、计算值、当前值、应用值
  • C学习-枚举(九)
  • HTTP--网络协议分层,http历史(二)
  • js数组之filter
  • laravel5.5 视图共享数据
  • SpriteKit 技巧之添加背景图片
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 翻译:Hystrix - How To Use
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 机器学习学习笔记一
  • 记一次和乔布斯合作最难忘的经历
  • 看域名解析域名安全对SEO的影响
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 前嗅ForeSpider中数据浏览界面介绍
  • 设计模式走一遍---观察者模式
  • 使用 QuickBI 搭建酷炫可视化分析
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • 组复制官方翻译九、Group Replication Technical Details
  • ​批处理文件中的errorlevel用法
  • #### go map 底层结构 ####
  • (3)nginx 配置(nginx.conf)
  • (33)STM32——485实验笔记
  • (WSI分类)WSI分类文献小综述 2024
  • (ZT)薛涌:谈贫说富
  • (笔试题)合法字符串
  • (二)windows配置JDK环境
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (六)软件测试分工
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • .equals()到底是什么意思?
  • .libPaths()设置包加载目录
  • .NET Framework与.NET Framework SDK有什么不同?
  • .NET 事件模型教程(二)
  • .NET 药厂业务系统 CPU爆高分析
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .net6 webapi log4net完整配置使用流程