• Linux高并发服务器开发—多线程


    多线程

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    线程操作的API

    在这里插入图片描述
    在这里插入图片描述

        一般情况下,main函数所在的线程我们称之为主线程(main线程),其余创建的线程
        称之为子线程。
        程序中默认只有一个进程,fork()函数调用,2进行
        程序中默认只有一个线程,pthread_create()函数调用,2个线程。
    
        #include <pthread.h>
        int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
        void *(*start_routine) (void *), void *arg);
    
            - 功能:创建一个子线程
            - 参数:
                - thread:传出参数,线程创建成功后,子线程的线程ID被写到该变量中。
                - attr : 设置线程的属性,一般使用默认值,NULL
                - start_routine : 函数指针,这个函数是子线程需要处理的逻辑代码
                - arg : 给第三个参数使用,传参
            - 返回值:
                成功:0
                失败:返回错误号。这个错误号和之前errno不太一样。
                获取错误号的信息:  char * strerror(int errnum);
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    
    #include <stdio.h>
    #include <pthread.h>
    #include <string.h>
    #include <unistd.h>
    
    void * callback(void * arg) {
        printf("child thread...\n");
        printf("arg value: %d\n", *(int *)arg);
        return NULL;
    }
    
    int main() {
    
        pthread_t tid;
    
        int num = 10;
    
        // 创建一个子线程
        int ret = pthread_create(&tid, NULL, callback, (void *)&num);
    
        if(ret != 0) {
            char * errstr = strerror(ret);
            printf("error : %s\n", errstr);
        } 
    
        for(int i = 0; i < 5; i++) {
            printf("%d\n", i);
        }
    
        sleep(1);
    
        return 0;   // 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

    在这里插入图片描述

    终止线程

    
        #include <pthread.h>
        void pthread_exit(void *retval);
            功能:终止一个线程,在哪个线程中调用,就表示终止哪个线程
            参数:
                retval:需要传递一个指针,作为一个返回值,可以在pthread_join()中获取到。
    
        pthread_t pthread_self(void);
            功能:获取当前的线程的线程ID
    
        int pthread_equal(pthread_t t1, pthread_t t2);
            功能:比较两个线程ID是否相等
            不同的操作系统,pthread_t类型的实现不一样,有的是无符号的长整型,有的
            是使用结构体去实现的。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    
    #include <stdio.h>
    #include <pthread.h>
    #include <string.h>
    
    void * callback(void * arg) {
        printf("child thread id : %ld\n", pthread_self());
        return NULL;    // pthread_exit(NULL);
    } 
    
    int main() {
    
        // 创建一个子线程
        pthread_t tid;
        int ret = pthread_create(&tid, NULL, callback, NULL);
    
        if(ret != 0) {
            char * errstr = strerror(ret);
            printf("error : %s\n", errstr);
        }
    
        // 主线程
        for(int i = 0; i < 5; i++) {
            printf("%d\n", i);
        }
    
        printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());
    
        // 让主线程退出,当主线程退出时,不会影响其他正常运行的线程。
        pthread_exit(NULL);
    
        printf("main thread exit\n");
    
        return 0;   // 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

    连接已终止的线程

        #include <pthread.h>
        int pthread_join(pthread_t thread, void **retval);
            - 功能:和一个已经终止的线程进行连接
                    回收子线程的资源
                    这个函数是阻塞函数,调用一次只能回收一个子线程
                    一般在主线程中使用
            - 参数:
                - thread:需要回收的子线程的ID
                - retval: 接收子线程退出时的返回值
            - 返回值:
                0 : 成功
                非0 : 失败,返回的错误号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    代码实现

    
    #include <stdio.h>
    #include <pthread.h>
    #include <string.h>
    #include <unistd.h>
    
    int value = 10;
    
    void * callback(void * arg) {
        printf("child thread id : %ld\n", pthread_self());
        // sleep(3);
        // return NULL; 
        // int value = 10; // 局部变量
        pthread_exit((void *)&value);   // return (void *)&value;
    } 
    
    int main() {
    
        // 创建一个子线程
        pthread_t tid;
        int ret = pthread_create(&tid, NULL, callback, NULL);
    
        if(ret != 0) {
            char * errstr = strerror(ret);
            printf("error : %s\n", errstr);
        }
    
        // 主线程
        for(int i = 0; i < 5; i++) {
            printf("%d\n", i);
        }
    
        printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());
    
        // 主线程调用pthread_join()回收子线程的资源
        int * thread_retval;
        ret = pthread_join(tid, (void **)&thread_retval);
    
        if(ret != 0) {
            char * errstr = strerror(ret);
            printf("error : %s\n", errstr);
        }
    
        printf("exit data : %d\n", *thread_retval);
    
        printf("回收子线程资源成功!\n");
    
        // 让主线程退出,当主线程退出时,不会影响其他正常运行的线程。
        pthread_exit(NULL);
    
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    间隔3S 回收子线程资源。
    在这里插入图片描述
    为什么pthread_join 要传递2级指针?

    好比说,int *a;修改这个数值,要传递一级指针。

    为了获得子线程退出时的返回值,所以join函数传入的是指向该返回值的指针,因为该返回值也是一个指针,所以传入的是二级指针。

    好比说 int a =10;
    change(int a);函数要对a进行改变,change(int a);无法修改a的数值。所以类型为 change(int *a);

    同理:int *a =10;
    change(int a);函数要对a进行改变,change(int *a);无法修改a的数值。所以类型为 change(int **a);

    线程分离

        #include <pthread.h>
        int pthread_detach(pthread_t thread);
            - 功能:分离一个线程。被分离的线程在终止的时候,会自动释放资源返回给系统。
              1.不能多次分离,会产生不可预料的行为。
              2.不能去连接一个已经分离的线程,会报错。
            - 参数:需要分离的线程的ID
            - 返回值:
                成功:0
                失败:返回错误号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    代码实例

    
    #include <stdio.h>
    #include <pthread.h>
    #include <string.h>
    #include <unistd.h>
    
    void * callback(void * arg) {
        printf("chid thread id : %ld\n", pthread_self());
        return NULL;
    }
    
    int main() {
    
        // 创建一个子线程
        pthread_t tid;
    
        int ret = pthread_create(&tid, NULL, callback, NULL);
        if(ret != 0) {
            char * errstr = strerror(ret);
            printf("error1 : %s\n", errstr);
        }
    
        // 输出主线程和子线程的id
        printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
    
        // 设置子线程分离,子线程分离后,子线程结束时对应的资源就不需要主线程释放
        ret = pthread_detach(tid);
        if(ret != 0) {
            char * errstr = strerror(ret);
            printf("error2 : %s\n", errstr);
        }
    
        // 设置分离后,对分离的子线程进行连接 pthread_join()
        // ret = pthread_join(tid, NULL);
        // if(ret != 0) {
        //     char * errstr = strerror(ret);
        //     printf("error3 : %s\n", errstr);
        // }
    
        pthread_exit(NULL);
    
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    线程分离之后再进行连接会出现error的情况。
    在这里插入图片描述

    线程取消

        #include <pthread.h>
        int pthread_cancel(pthread_t thread);
            - 功能:取消线程(让线程终止)
                取消某个线程,可以终止某个线程的运行,
                但是并不是立马终止,而是当子线程执行到一个取消点,线程才会终止。
                取消点:系统规定好的一些系统调用,我们可以粗略的理解为从用户区到内核区的切换,这个位置称之为取消点。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    取消某个线程

    #include <stdio.h>
    #include <pthread.h>
    #include <string.h>
    #include <unistd.h>
    
    void * callback(void * arg) {
        printf("chid thread id : %ld\n", pthread_self());
        for(int i = 0; i < 5; i++) {
            printf("child : %d\n", i);
        }
        return NULL;
    }
    
    int main() {
        
        // 创建一个子线程
        pthread_t tid;
    
        int ret = pthread_create(&tid, NULL, callback, NULL);
        if(ret != 0) {
            char * errstr = strerror(ret);
            printf("error1 : %s\n", errstr);
        }
    
        // 取消线程
        pthread_cancel(tid);
    
        for(int i = 0; i < 5; i++) {
            printf("%d\n", i);
        }
    
        // 输出主线程和子线程的id
        printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
    
        pthread_exit(NULL);
    
        return 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
    • 38

    多次运行结果是不一样的:某个点的时候终止子进程。
    在这里插入图片描述

    线程属性

        int pthread_attr_init(pthread_attr_t *attr);
            - 初始化线程属性变量
    
        int pthread_attr_destroy(pthread_attr_t *attr);
            - 释放线程属性的资源
    
        int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
            - 获取线程分离的状态属性
    
        int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
            - 设置线程分离的状态属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    线程属性代码实现

     
    
    #include <stdio.h>
    #include <pthread.h>
    #include <string.h>
    #include <unistd.h>
    
    void * callback(void * arg) {
        printf("chid thread id : %ld\n", pthread_self());
        return NULL;
    }
    
    int main() {
    
        // 创建一个线程属性变量
        pthread_attr_t attr;
        // 初始化属性变量
        pthread_attr_init(&attr);
    
        // 设置属性
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
        // 创建一个子线程
        pthread_t tid;
    
        int ret = pthread_create(&tid, &attr, callback, NULL);
        if(ret != 0) {
            char * errstr = strerror(ret);
            printf("error1 : %s\n", errstr);
        }
    
        // 获取线程的栈的大小
        size_t size;
        pthread_attr_getstacksize(&attr, &size);
        printf("thread stack size : %ld\n", size);
    
        // 输出主线程和子线程的id
        printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
    
        // 释放线程属性资源
        pthread_attr_destroy(&attr);
    
        pthread_exit(NULL);
    
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    在这里插入图片描述

    线程同步

    线程间同步的引入示例:

    局部变量的时候:

    #include <stdio.h>
    #include<pthread.h>
    
    
    void  *sellticket(void *arg)
    {
        //卖票
        int ticket = 100;
        while (ticket > 0){
    
            printf("%ld正在卖第%d 张门票\r\n",pthread_self(),ticket);
            ticket--;
        
        }
        return NULL;
    }
    
    
    int main()
    {
    
        //创建3个子线程
        pthread_t pid[3];
        for (int i = 0; i < 3; i++)
        {
            pthread_create(&pid[i],NULL,sellticket,NULL);
        }
    
        //回收资源的方法1:回收子线程的资源,阻塞函数
        for (int i = 0; i < 3; i++)
        {
            pthread_join(pid[i],NULL);
        }
        
        //回收资源的方法2:设置线程分离
    
        // for (int i = 0; i < 3; i++)
        // {
        //     pthread_detach(&pid[i]);
        // }
    
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在这里插入图片描述

    在这里插入图片描述

    互斥锁

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    互斥量的API

    
        互斥量的类型 pthread_mutex_t
        int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
            - 初始化互斥量
            - 参数 :
                - mutex : 需要初始化的互斥量变量
                - attr : 互斥量相关的属性,NULL
            - restrict : C语言的修饰符,被修饰的指针,不能由另外的一个指针进行操作。
                pthread_mutex_t *restrict mutex = xxx;
                pthread_mutex_t * mutex1 = mutex;
    
        int pthread_mutex_destroy(pthread_mutex_t *mutex);
            - 释放互斥量的资源
    
        int pthread_mutex_lock(pthread_mutex_t *mutex);
            - 加锁,阻塞的,如果有一个线程加锁了,那么其他的线程只能阻塞等待
    
        int pthread_mutex_trylock(pthread_mutex_t *mutex);
            - 尝试加锁,如果加锁失败,不会阻塞,会直接返回。
    
        int pthread_mutex_unlock(pthread_mutex_t *mutex);
            - 解锁
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    互斥量实现原子卖票

    
    #include <stdio.h>
    #include <pthread.h>
    #include <unistd.h>
    
    // 全局变量,所有的线程都共享这一份资源。
    int tickets = 1000;
    
    // 创建一个互斥量
    pthread_mutex_t mutex;
    
    void * sellticket(void * arg) {
    
        // 卖票
        while(1) {
    
            // 加锁
            pthread_mutex_lock(&mutex);
    
            if(tickets > 0) {
                usleep(6000);
                printf("%ld 正在卖第 %d 张门票\n", pthread_self(), tickets);
                tickets--;
            }else {
                // 解锁
                pthread_mutex_unlock(&mutex);
                break;
            }
    
            // 解锁
            pthread_mutex_unlock(&mutex);
        }
    
        
    
        return NULL;
    }
    
    int main() {
    
        // 初始化互斥量
        pthread_mutex_init(&mutex, NULL);
    
        // 创建3个子线程
        pthread_t tid1, tid2, tid3;
        pthread_create(&tid1, NULL, sellticket, NULL);
        pthread_create(&tid2, NULL, sellticket, NULL);
        pthread_create(&tid3, NULL, sellticket, NULL);
    
        // 回收子线程的资源,阻塞
        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
        pthread_join(tid3, NULL);
    
        pthread_exit(NULL); // 退出主线程
    
        // 释放互斥量资源
        pthread_mutex_destroy(&mutex);
    
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    结果:导致一个问题,由同一个线程执行卖票操作。
    在这里插入图片描述
    临界区代码修改:

    void  *sellticket(void *arg)
    {
        //卖票
        while (1){
    
             //加锁
            pthread_mutex_lock(&mutex);
                if (ticket > 0)
                {
                usleep(5000);
                printf("%ld正在卖第%d 张门票\r\n",pthread_self(),ticket);
                ticket--;
                }
            else 
                {
                pthread_mutex_unlock(&mutex);
                break;
                }
             //解锁
            pthread_mutex_unlock(&mutex);
        }
       
        return NULL;
    }
    
    
    • 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

    在这里插入图片描述

    死锁

    死锁产生的几种场景:
    1、忘记释放锁;
    在这里插入图片描述

    在这里插入图片描述

    2、重复加锁;

    在这里插入图片描述
    程序阻塞住了。
    在这里插入图片描述

    3、多线程多锁,抢占锁的资源;

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    读写锁

    读写锁的应用场景:对共享区的读的操作,大于写的操作。

    在这里插入图片描述

    在这里插入图片描述

    条件变量

    在这里插入图片描述

        读写锁的类型 pthread_rwlock_t
        int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
        int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
        int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
        int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
        int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
        int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
        int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    读写锁案例

    	案例:8个线程操作同一个全局变量。
        3个线程不定时写这个全局变量,5个线程不定时的读这个全局变量
    
    #include <stdio.h>
    #include <pthread.h>
    #include <unistd.h>
    
    // 创建一个共享数据
    int num = 1;
    // pthread_mutex_t mutex;
    pthread_rwlock_t rwlock;
    
    void * writeNum(void * arg) {
    
        while(1) {
            pthread_rwlock_wrlock(&rwlock);
            num++;
            printf("++write, tid : %ld, num : %d\n", pthread_self(), num);
            pthread_rwlock_unlock(&rwlock);
            usleep(100);
        }
    
        return NULL;
    }
    
    void * readNum(void * arg) {
    
        while(1) {
            pthread_rwlock_rdlock(&rwlock);
            printf("===read, tid : %ld, num : %d\n", pthread_self(), num);
            pthread_rwlock_unlock(&rwlock);
            usleep(100);
        }
    
        return NULL;
    }
    
    int main() {
    
       pthread_rwlock_init(&rwlock, NULL);
    
        // 创建3个写线程,5个读线程
        pthread_t wtids[3], rtids[5];
        for(int i = 0; i < 3; i++) {
            pthread_create(&wtids[i], NULL, writeNum, NULL);
        }
    
        for(int i = 0; i < 5; i++) {
            pthread_create(&rtids[i], NULL, readNum, NULL);
        }
    
        // 设置线程分离
        for(int i = 0; i < 3; i++) {
           pthread_detach(wtids[i]);
        }
    
        for(int i = 0; i < 5; i++) {
             pthread_detach(rtids[i]);
        }
    
        pthread_exit(NULL);
    
        pthread_rwlock_destroy(&rwlock);
    
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    在这里插入图片描述

    生产者和消费者模型

    在这里插入图片描述在这里插入图片描述

    生产者消费者原始模型

    #include <stdio.h>
    #include <pthread.h>
    #include <stdlib.h>
    #include <unistd.h>
    // 创建一个互斥量
    pthread_mutex_t mutex;
    typedef struct Node{
        int num;
        struct Node *next;
    }Nodelist;
    // 头结点
    struct Node * head = NULL;
    
    void *producer(void *arg)
    {
        //不断添加新的节点
        while (1)
        {
                pthread_mutex_lock(&mutex);
                Nodelist *newNode = (Nodelist *)malloc(sizeof(Nodelist));
                newNode->next = head;
                head = newNode;
                newNode->num = rand()%1000;
                printf("add node,num :%d,tid:%ld\n",newNode->num,pthread_self());
                pthread_mutex_unlock(&mutex);
                usleep(100);
        }
    }
    
    void *customer(void *arg)
    {
        while (1)
        {
            pthread_mutex_lock(&mutex);
            //保存头结点指针
            Nodelist *tmp = head;
            if (head != NULL)
            {
                head = head->next;
                printf("delete node num:%d,tid :%ld\n",tmp->num,pthread_self());
                free(tmp);
                pthread_mutex_unlock(&mutex);
    
            }else{
                pthread_mutex_unlock(&mutex);
            } 
            usleep(100);
        }
    }
    
    int main()
    {
    
        pthread_mutex_init(&mutex, NULL);
    
        // 创建5个生产者线程,和5个消费者线程
        pthread_t ptids[5], ctids[5];
    
        for(int i = 0; i < 5; i++) {
            pthread_create(&ptids[i], NULL, producer, NULL);
            pthread_create(&ctids[i], NULL, customer, NULL);
        }
    
        for(int i = 0; i < 5; i++) {
            pthread_detach(ptids[i]);
            pthread_detach(ctids[i]);
        }
            while(1) {
            sleep(10);
        }
        pthread_mutex_destroy(&mutex);
        pthread_exit(NULL);
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    条件变量

    条件变量不是锁,某个条件满足之后,线程阻塞,或者某个条件满足之后,线程解除阻塞。

    
        条件变量的类型 pthread_cond_t
        int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
        int pthread_cond_destroy(pthread_cond_t *cond);
        int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
            - 等待,调用了该函数,线程会阻塞。
        int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
            - 等待多长时间,调用了这个函数,线程会阻塞,直到指定的时间结束。
        int pthread_cond_signal(pthread_cond_t *cond);
            - 唤醒一个或者多个等待的线程
        int pthread_cond_broadcast(pthread_cond_t *cond);
            - 唤醒所有的等待的线程
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    条件变量对生产者消费者线程进行改进

    #include <stdio.h>
    #include <pthread.h>
    #include <stdlib.h>
    #include <unistd.h>
    // 创建一个互斥量
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    typedef struct Node{
        int num;
        struct Node *next;
    }Nodelist;
    // 头结点
    struct Node * head = NULL;
    void *producer(void *arg)
    {
        //不断添加新的节点
        while (1)
        {
                pthread_mutex_lock(&mutex);
                Nodelist *newNode = (Nodelist *)malloc(sizeof(Nodelist));
                newNode->next = head;
                head = newNode;
                newNode->num = rand()%1000;
                printf("add node,num :%d,tid:%ld\n",newNode->num,pthread_self());
                //只要生产一个就调用消费者消费。唤醒在等待的消费者pthread_cond_wait
                pthread_cond_signal(&cond);
                pthread_mutex_unlock(&mutex);
                usleep(100);
        }  
    }
    
    void *customer(void *arg)
    {
        while (1)
        {
            pthread_mutex_lock(&mutex);
            //保存头结点指针
            Nodelist *tmp = head;
            if (head != NULL)
            {
                head = head->next;
                printf("delete node num:%d,tid :%ld\n",tmp->num,pthread_self());
                free(tmp);
                pthread_mutex_unlock(&mutex);
            }else{
                //没有数据,需要等待,阻塞在这
                //当这个函数调用阻塞的时候,会对互斥锁进行解锁,当不阻塞的时候,继续向下执行会重新加锁。
                pthread_cond_wait(&cond,&mutex);
                pthread_mutex_unlock(&mutex);
            }
            usleep(100);
        }
    }
    
    int main()
    {
    
        pthread_mutex_init(&mutex, NULL);
    
        pthread_cond_init(&cond,NULL);
    
        // 创建5个生产者线程,和5个消费者线程
        pthread_t ptids[5], ctids[5];
    
        for(int i = 0; i < 5; i++) {
            pthread_create(&ptids[i], NULL, producer, NULL);
            pthread_create(&ctids[i], NULL, customer, NULL);
        }
    
        for(int i = 0; i < 5; i++) {
            pthread_detach(ptids[i]);
            pthread_detach(ctids[i]);
        }
    
    
            while(1) {
            sleep(10);
        }
    
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);
    
        pthread_exit(NULL);
    
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    信号量

    在这里插入图片描述

    
        信号量的类型 sem_t
        int sem_init(sem_t *sem, int pshared, unsigned int value);
            - 初始化信号量
            - 参数:
                - sem : 信号量变量的地址
                - pshared : 0 用在线程间 ,非0 用在进程间
                - value : 信号量中的值
    
        int sem_destroy(sem_t *sem);
            - 释放资源
    
        int sem_wait(sem_t *sem);
            - 对信号量加锁,调用一次对信号量的值-1,如果值为0,就阻塞
    
        int sem_trywait(sem_t *sem);
    
        int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
        int sem_post(sem_t *sem);
            - 对信号量解锁,调用一次对信号量的值+1
    
        int sem_getvalue(sem_t *sem, int *sval);
    
        sem_t psem;
        sem_t csem;
        init(psem, 0, 8);
        init(csem, 0, 0);
    
        producer() {
            sem_wait(&psem);
            sem_post(&csem)
        }
    
        customer() {
            sem_wait(&csem);
            sem_post(&psem)
        }
    
    
    
    • 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

    生产者消费者模型

    
    
    #include <stdio.h>
    #include <pthread.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <semaphore.h>
    
    // 创建一个互斥量
    pthread_mutex_t mutex;
    // 创建两个信号量
    sem_t psem;
    sem_t csem;
    
    struct Node{
        int num;
        struct Node *next;
    };
    
    // 头结点
    struct Node * head = NULL;
    
    void * producer(void * arg) {
    
        // 不断的创建新的节点,添加到链表中
        while(1) {
            sem_wait(&psem);
            pthread_mutex_lock(&mutex);
            struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
            newNode->next = head;
            head = newNode;
            newNode->num = rand() % 1000;
            printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
            pthread_mutex_unlock(&mutex);
            sem_post(&csem);
        }
    
        return NULL;
    }
    
    void * customer(void * arg) {
    
        while(1) {
            sem_wait(&csem);
            pthread_mutex_lock(&mutex);
            // 保存头结点的指针
            struct Node * tmp = head;
            head = head->next;
            printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
            free(tmp);
            pthread_mutex_unlock(&mutex);
            sem_post(&psem);
           
        }
        return  NULL;
    }
    
    int main() {
    
        pthread_mutex_init(&mutex, NULL);
        sem_init(&psem, 0, 8);
        sem_init(&csem, 0, 0);
    
        // 创建5个生产者线程,和5个消费者线程
        pthread_t ptids[5], ctids[5];
    
        for(int i = 0; i < 5; i++) {
            pthread_create(&ptids[i], NULL, producer, NULL);
            pthread_create(&ctids[i], NULL, customer, NULL);
        }
    
        for(int i = 0; i < 5; i++) {
            pthread_detach(ptids[i]);
            pthread_detach(ctids[i]);
        }
    
        while(1) {
            sleep(10);
        }
    
        pthread_mutex_destroy(&mutex);
    
        pthread_exit(NULL);
    
        return 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
  • 相关阅读:
    Toolformer论文阅读笔记(简略版)
    Java Lambda表达式的使用
    定时器+按键控制LED流水灯模式+定时器时钟——“51单片机”
    代码随想录算法训练营第十一天|回溯!组合数!
    计算机网络_计算机的概念、组成、功能、分类
    【算法】一文带你从浅至深入门dp动态规划
    SQL函数之分割
    AVS3:双向梯度修正BGC
    自适应蝴蝶算法附matlab代码IBOA
    电磁场知识回顾——基本概念
  • 原文地址:https://blog.csdn.net/m0_46152793/article/details/125586856