• 【Linux-day11-线程的创建与同步】


    Linux 线程的创建与同步

    线程的概念

    线程是进程内部的一条执行序列或执行路径,一个进程可以包含多条线程。

    进程与线程的区别
    • 进程是资源分配的最小单位,线程是 CPU 调度的最小单位
    • 进程有自己的独立地址空间,线程共享进程中的地址空间
    • 进程的创建消耗资源大,线程的创建相对较小
    • 进程的切换开销大,线程的切换开销相对较小
    线程的实现方式

    操作系统中,线程的实现有以下三种方式:

    • 内核级线程
    • 用户级线程
    • 组合级线程

    Linux 中线程的实现

    Linux 实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。Linux 把
    所有的线程都当做进程来实现。内核并没有准备特别的调度算法或是定义特别的数据结构来
    表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有唯
    一隶属于自己的 task_struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和
    其他一些进程共享某些资源,如地址空间)。

    线程库中的接口介绍
    pthread_create()用于创建线程

    int pthread_create(pthread_t *thread, **const ** pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

    成功返回 0, 失败返回错误码

    thread: 接收创建的线程的 ID

    attr: 指定线程的属性

    start_routine: 指定线程函数

    arg: 给线程函数传递的参数

    pthread_exit()退出线程

    int pthread_exit(void *retval);

    pthread_exit()退出线程

    retval:指定退出信息

    pthread_join()等待 thread 指定的线程退出,线程未退出时,该方法阻塞

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

    retval:接收 thread 线程退出时,指定的退出信息

    测试代码1
    #include
    #include
    #include
    #include
    
    void* fun(void* arg)
    {
        int* p = (int*)arg;
        int val = *p;
            printf("%d\n",val);
        return NULL;
    }
    
    int main()
    {
        pthread_t id[5];
        int i=0;
        for(;i<5;i++)
        {
            pthread_create(&id[i],NULL,fun,(void*)&i);
        }
        for(i=0;i<5;i++)
        {
            pthread_join(id[i],NULL);
        }
        exit(0);
    }
    
    • 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

    预估:会出现0 1 2 3 4按一定顺序

    结果如图:

    我们发现结果和预想的不一样,这是因为线程是并发运行的,主线程main()和5个副线程都在同时运行,fun是通过指针解引用,主线程会随时改变i的值 ; pthread_create(&id[i],NULL,fun,(void*)&i);只是向内核申请,不一定顺序批准。

    测试代码2
    #include
    #include
    #include
    #include
    int val = 0;
    void* fun(void* arg)
    {
        for(int i=0;i<1000;i++)
        {
            printf("%d\n",++val);
        }
        return NULL;
    }
    int main()
    {
        pthread_t id[5];
        int i=0;
        for(;i<5;i++)
        {
            pthread_create(&id[i],NULL,fun,(void*)&i);
        }
        for(i=0;i<5;i++)
        {
            pthread_join(id[i],NULL);
        }
        exit(0);
    }
    
    • 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

    预期: 输出5000

    结果如图:

    这是因为线程是并发执行的,上一个线程对val进行++完,还没有写回内存,下个进程读取了之前的值对其++;

    线程同步

    1. 信号量

    函数介绍

    int sem_init(sem_t * sem, int pshared,unsigned int value)

    sem: 指向的信号量对象

    pshared: 0表示此信号为当前进程局部的,否则为多个进程间共享

    value: 设置信号的值

    int sem_wait(sem_t * sem)

    进行p操作,信号值减1

    sem: 指向的信号量对象

    int sem_post(sem_t * sem)

    进行v操作,信号值加1

    sem: 指向的信号量对象

    int sem_destory(sem_t * sem)

    j清理该信号拥有的所有资源,成功返回0

    sem: 指向的信号量对象

    下面使用该方法对测试代码2修改

    #include
    #include
    #include
    #include
    #include
    
    int val = 0;
    sem_t sig;
    void* fun(void* arg)
    {
        for(int i=0;i<1000;i++)
        {
            sem_wait(&sig);
            printf("%d\n",++val);
            sem_post(&sig);
        }
        return NULL;
    }
    int main()
    {
        pthread_t id[5];
        sem_init(&sig,0,1);
        int i=0;
        for(;i<5;i++)
        {
            pthread_create(&id[i],NULL,fun,(void*)&i);
        }
        for(i=0;i<5;i++)
        {
            pthread_join(id[i],NULL);
        }
        sem_destroy(&sig);
        exit(0);
    }
    
    • 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

    结果如图:

    2.互斥锁

    接口介绍:

    1.int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t * mutexattr);

    初始化锁
    mutex: 互斥变量的指针
    mutexattr: 设置互斥锁的属性

    2.int pthread_mutex_lock(pthread_mutex_t* mutex);

    上锁(加锁)

    3.int pthread_mutex_unlock(pthread_mutex_t* mutex);

    解锁

    4.int pthread_mutex_destroy(pthread_mutex_t* mutex);

    销毁锁

    使用此方法修改测试代码2

    #include
    #include
    #include
    #include
    #include
    
    
    int val = 0;
    pthread_mutex_t mutex;
    void* fun(void* arg)
    {
        for(int i=0;i<1000;i++)
        {
            pthread_mutex_lock(&mutex);
            printf("%d\n",++val);
            pthread_mutex_unlock(&mutex);
        }
        return NULL;
    }
    
    int main()
    {
        pthread_t id[5];
        pthread_mutex_init(&mutex,NULL);
        int i=0;
        for(;i<5;i++)
        {
            pthread_create(&id[i],NULL,fun,(void*)&i);
        }
        for(i=0;i<5;i++)
        {
            pthread_join(id[i],NULL);
        }
        pthread_mutex_destroy(&mutex);
        exit(0);
    
    }
    
    • 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

    结果如图:

  • 相关阅读:
    apache poi excel export
    Qml-跨窗口拖动图片、物体
    鸿蒙OS app开发环境搭建
    交换机端口映射
    NSIC2050JBT3G 车规级120V 50mA ±15% 用于LED照明的线性恒流调节器(CCR) 增强汽车安全
    SpringBoot SpringBoot 开发实用篇 6 监控 6.4 info 端点指标控制
    北理工嵩天Python语言程序设计笔记(9 程序设计方法学)
    使用Spring Boot和JPA创建GraphQL API
    卷积神经网络及YOLO算法 相关
    PMP考前冲刺
  • 原文地址:https://blog.csdn.net/qq_63282188/article/details/132865895