Linux多线程并发运行原理+代码例程详解

线程创建和退出

原理

在这里插入图片描述

代码

交叉编译

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>


/*thread1*/
void thread1(void)
{
    int i = 0;
    for(i = 0; i < 6; i++) {
        printf("This is a pthread1. \n");
        if(i == 2){
            pthread_exit(0);
        }
        sleep(1);
    }
}


/*thread2*/
void thread2(void)
{
    int i;
    for(i = 0; i < 3; i++){
        printf("This is a pthread2. \n");   
    }
    pthread_exit(0);
}

/*main*/
int main(void)
{
    pthread_t id1, id2;
    int i,ret;
    /*create thread1*/
    ret = pthread_create(&id1, NULL, (void *) thread1, NULL);
    if(ret != 0){
        printf("Create pthread1 error!\n");
        exit(1);
    }
    /*create thread2*/
    ret = pthread_create(&id2, NULL, (void *) thread2, NULL);
    if(ret != 0) {
        printf("Create pthread2 error!\n");
        exit(1);
    }
    /*wait for thread to end*/
    pthread_join(id1, NULL);
    pthread_join(id2, NULL);
    exit(0);

    return 0;
}

执行

$CC thread_test.c

结果报错
在这里插入图片描述
出现编译错误undefined reference to ‘pthread_create’。由于pthread库不是标准linux库, 所以出错。 找不到函数库,通过静态调用,-lpthread,就可以编译通过

 $CC thread_test.c -o thread_test.0 -lpthread

在这里插入图片描述
然后通过scp命令发送可执行文件到开发板

scp thread_test root@192.168.0.232:/tmp

在这里插入图片描述

结果

在开发板中执行
在这里插入图片描述

线程属性修改

原理

在这里插入图片描述

代码

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

/*thread1*/
void thread1(void)
{
    int i = 0;
    for(i = 0; i < 6; i++) {
        printf("This is a pthread1. \n");
        if(i == 2){
            pthread_exit(0);
        }
        sleep(1);
    }
}


/*thread2*/
void thread2(void)
{
    int i;
    while(1){
        for(i = 0; i < 3; i++){
            printf("This is a pthread2. \n");  
            sleep(1); 
        }
    }
    
    /* void pthread_exit(void *retval)
     * Retval:pthread_exit()调用者线程的返回值,可由其他函数如pthread_join 来检索获取 
     */
    pthread_exit(0);
}

/*main*/
int main(void)
{
    pthread_t id1, id2;
    int i,ret;
    pthread_attr_t attr;

    /*初始化线程 */
    pthread_attr_init(&attr);

    /*设置线程绑定属性*/
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

    /*设置线程分离属性*/
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

    /* create thread1 创建线程
     * int pthread_create ((pthread_t *thread, pthread_attr_t *attr,void *(*start_routine)(void *), void *arg)) 
     * thread:线程标识符
     * attr:线程属性设置
     * start_routine:线程函数的起始地址
     * arg:传递给start_routine的参数
     * return:成功:0 出错:-1
     */
    ret=pthread_create(&id1,&attr,(void *) thread1,NULL);
    if(ret != 0){
        printf("Create pthread1 error!\n");
        exit(1);
    }
    /*create thread2*/
    ret = pthread_create(&id2, NULL, (void *) thread2, NULL);
    if(ret != 0) {
        printf("Create pthread2 error!\n");
        exit(1);
    }
    /*wait for thread to end*/
    /* int pthread_join ((pthread_t th, void **thread_return))
     * th:等待线程的标识符
     * thread_return:用户定义的指针,用来存储被等待线程的返回值(不为NULL时)
     * return:成功:0 出错:-1
     */
    // pthread_join(id1, NULL);                                        // pthread_join将当前线程挂起,等待线程的结束
    pthread_join(id2, NULL);
    // exit(0);                                                        // 所有线程都终止

    return 0;
}

这次不放在嵌入式开发板上运行了,所以就用gcc编译了

 gcc thread_test2.c -o thread_test2 -lpthread

结果

直接在wsl中运行编译好的可执行文件thread_test2
在这里插入图片描述
因为有个while死循环的存在,所以程序就会一直运行下去,我们ctrl+c来结束打印,然后输入free

使用“free”命令查看内存使用情况

这是程序运行之前的内存使用情况uesd
在这里插入图片描述
这是程序运行之后的内存使用情况used
在这里插入图片描述

互斥锁

原理

在这里插入图片描述

代码

/*
 * 使用互斥锁来实现对变量lock_var 的加一、打印操作
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;                                              // 创建快速互斥锁
int lock_var;
time_t end_time;

void pthread1(void *arg);
void pthread2(void *arg);

int main(int argc, char *argv[])
{
    pthread_t id1,id2;
    pthread_t mon_th_id;
    int ret;

    end_time = time(NULL) + 10;

    /* 互斥锁初始化
     * int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
     * Mutex:互斥锁
     * Mutexattr:PTHREAD_MUTEX_INITIALIZER:创建快速互斥锁;PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:创建递归互斥锁;PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:创建检错互斥锁
     * return:成功:0 出错:-1
     */
    pthread_mutex_init(&mutex, NULL);

    /*创建两个线程*/
    ret = pthread_create(&id1, NULL, (void *)pthread1, NULL);
    if(ret != 0){
        perror("pthread cread1");
    }
    ret = pthread_create(&id2, NULL, (void *)pthread2, NULL);
    if(ret != 0){
        perror("pthread cread2");
    }
    pthread_join(id1, NULL);
    pthread_join(id2, NULL);
    exit(0);
}

void pthread1(void *arg)
{
    int i;
    while(time(NULL) < end_time){
        /* 
         * int pthread_mutex_lock(pthread_mutex_t *mutex,)
           int pthread_mutex_trylock(pthread_mutex_t *mutex,)
           int pthread_mutex_unlock(pthread_mutex_t *mutex,)
           int pthread_mutex_destroy(pthread_mutex_t *mutex,)
         * Mutex:互斥锁
         * return:成功:0 出错:-1
         */
        /*互斥上锁*/
        if(pthread_mutex_lock(&mutex) != 0){                            
            perror("pthread_mutex_lock");
        }else{
            printf("pthread1:pthread1 lock the variable\n");
        }

        for(i = 0; i < 0; i++){
            sleep(1);
            lock_var++;
        }
        /*互斥锁解锁*/
        if(pthread_mutex_unlock(&mutex) != 0){
            perror("pthread_mutex_unlock\n");
        }else{
            printf("pthread1:pthread1 unlock the variable\n");
        }
        sleep(1);
    }
}

void pthread2(void *arg)
{
    int nolock = 0;
    int ret;
    while(time(NULL) < end_time){
        /*测试互斥锁*/
        ret = pthread_mutex_trylock(&mutex);
        if(ret == EBUSY){
            printf("pthread2:the variable is locked by pthread1\n");
        }else{
            if(ret != 0){
                perror("pthread_mutex_trylock");
                exit(1);
            }else{
                printf("pthread2:pthread2 got lock. The variable is %d\n", lock_var);
            }
            /*互斥锁解锁*/
            if(pthread_mutex_unlock(&mutex) != 0){
                perror("pthread_mutex_unlock");
            }else{
                printf("pthread2:pthread2 unlock the variable\n");
            }
        }
        sleep(3);
    }
}

代码是什么意思呢?
在这里插入图片描述
两个线程并发,线程一定时while循环,锁定时候线程二检测到上锁然后不运行服务函数,检测到解锁后运行服务函数输出

编译代码
在这里插入图片描述

结果

运行可执行文件
在这里插入图片描述

信号量线程控制原理

在这里插入图片描述

使用信号量线程互斥

使用同一个信号量sem,sem初始值设置为0,进程一执行P操作,sem-1=-1,所以该进程就将阻塞直到信号量sem大于等于0为止。

代码

/*
 * 信号量线程控制
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>

int lock_var;
time_t end_time;
sem_t sem;

void pthread1(void *arg);
void pthread2(void *arg);

int main(int argc, char *argv[])
{
    pthread_t id1, id2;
    pthread_t mon_th_id;
    int ret;
    end_time = time(NULL) + 30;

    /*初始化信号量为1*/
    ret = sem_init(&sem, 0, 1);
    if(ret != 0){
        perror("sem_init");
    }

    /*创建两个线程*/
    ret = pthread_create(&id1, NULL, (void *)pthread1, NULL);
    if(ret != 0){
        perror("pthread cread1");
    }
    ret = pthread_create(&id2, NULL, (void *)pthread2, NULL);
    if(ret != 0){
        perror("pthread cread2");
    }
    
    pthread_join(id1, NULL);
    pthread_join(id2, NULL);

    exit(0);
}

void pthread1(void *arg)
{
    int i;
    while(time(NULL) < end_time){
        /*信号量减一,p操作*/
        sem_wait(&sem);
        for(i = 0; i < 2; i++){
            sleep(1);
            lock_var++;
            printf("lock_var = %d\n", lock_var);
        }
        printf("pthread1:lock_var = %d\n", lock_var);

        /*信号量加一,v操作*/
        sem_post(&sem);
        sleep(1);
    }
}

void pthread2(void *arg)
{
    int nolock = 0;
    int ret;
    while(time(NULL) < end_time){
        /*信号量减一,p操作*/
        sem_wait(&sem);
        printf("pthread2:pthread1 got lock; lock_var = %d\n", lock_var);

        /*信号量加一,v操作*/
        sem_post(&sem);
        sleep(3);
    }
}

在这里插入图片描述

结果

运行结果如图所示
在这里插入图片描述

在输出的结果中,可以很清晰的看到,进程一执行了P操作后,线程二就被阻塞了不能执行,等待线程一执行完for循环过了2s之后,线程一执行V操作,sem=0,然后再等待一秒钟

sem现在大于等于0,所以该进程具有公共资源的访问权限,线程二可以执行V操作修改sem的值了,sem=-1,线程之间又发生了互斥,线程二执行输出,然后sleep 3秒钟。

使用信号量线程同步

在这里插入图片描述

代码

/*
 * 通过两个信号量来实现两个线程间的同步
 * 程序先运行线程二,再运行线程一
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>

int lock_var;
time_t end_time;
sem_t sem1, sem2;

void pthread1(void *arg);
void pthread2(void *arg);

int main(int argc, char *argv[])
{
    pthread_t id1, id2;
    pthread_t mon_th_id;
    int ret;
    end_time = time(NULL) + 30;

    /*初始化两个信号量,一个信号量为1,一个信号量为0*/
    ret = sem_init(&sem1, 0, 1);
    ret = sem_init(&sem2, 0, 0);
    if(ret != 0){
        perror("sem_init");
    }

    /*创建两个线程*/
    ret = pthread_create(&id1, NULL, (void *)pthread1, NULL);
    if(ret != 0){
        perror("pthread cread1");
    }
    ret = pthread_create(&id2, NULL, (void *)pthread2, NULL);
    if(ret != 0){
        perror("pthread cread2");
    }

    pthread_join(id1, NULL);
    pthread_join(id2, NULL);

    exit(0);
}

void pthread1(void *arg)
{
    int i;
    while(time(NULL) < end_time){
        /*P操作信号量sem2 = 0 信号量-1*/
        sem_wait(&sem2);//现在sem2=-1,该线程阻塞,直到sem2的值大于等于零为止,也就是进程二执行V操作sem2=0
        for(i = 0; i < 2; i++){
            sleep(1);
            lock_var++;
            printf("lock_var = %d\n", lock_var);
        }
        printf("pthread1:lock_var = %d\n", lock_var);

        /*V 操作信号量sem1 = 0 +1*/
        sem_post(&sem1);//sem1 = 1
        sleep(1);
    }
}

void pthread2(void *arg)
{
    int nolock = 0;
    int ret;
    while(time(NULL) < end_time){
        /*P 操作信号量sem1 = 1 信号量-1*/
        sem_wait(&sem1);//现在sem1=0 该线程具有公共资源的访问权限,执行
        printf("pthread2:pthread1 got lock; lock_var = %d\n", lock_var);

        /*V 操作信号量sem2 = -1 +1*/
        sem_post(&sem2);//现在sem2=0,所以线程一就可以运行了
        sleep(3);
    }
}

结果

从以下结果中可以看出,该程序确实实现了先运行线程二,再运行线程一。
在这里插入图片描述

小结分析

对于线程一和线程二都在3s 的一个周期内进行并行的操作,因为有两个信号量,所以可以分别控制
在这里插入图片描述

在第一个时间中,sem2 = -1,sem1 = 0;所以线程二可以执行,线程一此时被阻塞, 需要等待sem2 >= 0 的状态。线程二执行了自己的输出pthread2:pthread1 got lock; lock_var = 0,然后执行V操作,sem2 = 0,这时候线程一满足了执行的条件,执行设定的输出lock_var = 1。这时候线程二也在运行,执行sleep(3),也就是延迟3s,只是这个过程没有了其他的输出。

在第二个时间中,因为满足了sem2 >= 0 的条件,所以继续执行lock_var = 2,并且结束了for循环,执行输出pthread1:lock_var = 2,而于此同时线程二依然在sleep中。

在第三个时间中,线程一执行了V操作,sem1 = 1,时间也来到了第三秒,一个时间周期完成。

“生产者消费者”实验

问题分析

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

代码

/*
 * 有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲
   区中拿走产品。当一个生产者在缓冲区满时必须等待,当一个消费者在缓冲区空时页必须等
   待。
 * 这里使用3个信号量,其中两个信号量avail和full分别用于解决生产者和消费者线程之
   间的同步问题,mutex是用于这两个线程之间的互斥问题。其中avail初始化为N(有界缓冲 
   区的空单元数),mutex 初始化为1,full初始化为0。
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#include <string.h>

#define FIFO        "myfifo"
#define N           5

int lock_var = 0;
int fd;
char buf_r[100];

sem_t mutex, full, avail;
time_t end_time;

void productor(void *arg);
void consumer(void *arg);
void pthread1(void *arg);
void pthread2(void *arg);

/*主函数*/
int main(int argc, char *argv[])
{
    pthread_t id1, id2;
    pthread_t mon_th_id;

    int ret;

    end_time = time(NULL) + 30;

    /*创建有名管道*/
    if((mkfifo(FIFO, O_CREAT|O_EXCL) < 0) && (errno != EEXIST)){
        printf("cannot create fifoserver\n");
    }
    printf("Preparing for reading bytes...\n");
    memset(buf_r, 0, sizeof(buf_r));

    /*打开管道*/
    fd = open(FIFO, O_RDWR|O_NONBLOCK, 0);                          // 新建文件myfifo
    if(fd == -1){
        perror("open");
        exit(1);
    }

    /*初始化互斥信号量为1*/
    ret = sem_init(&mutex, 0, 1);

    /*初始化avail信号量为N*/
    ret = sem_init(&avail, 0, N);

    /*初始化full信号量为0*/
    ret = sem_init(&full, 0, 0);
    if(ret != 0){
        perror("sem_init");
    }

    /*创建两个线程*/
    ret = pthread_create(&id1, NULL, (void *)productor, NULL);
    if(ret != 0){
        perror("pthread cread1");
    }

    ret = pthread_create(&id2, NULL, (void *)consumer, NULL);
    if(ret != 0){
        perror("pthread cread2");
    }

    pthread_join(id1, NULL);
    pthread_join(id2, NULL);

    exit(0);
}

/*生产者线程*/
void productor(void *arg)
{
    int i, nwrite;
    while(time(NULL) < end_time){
        /*P操作信号量avail和mutex*/
        sem_wait(&avail);                                           // avail= N-1   
        sem_wait(&mutex);                                           // mutex= 1-1 =0 
        /*生产者写入数据*/
        if((nwrite = write(fd, "hello", 5)) == -1){                 // 字符数据“hello”写入到fd文件中
            if(errno == EAGAIN){
                printf("The FIFO has not been read yet. Please try later\n");
            }
        }else{
                printf("write hello to the FIFO\n");
        }

        /*V操作信号量full和mutex*/
        sem_post(&full);                                            // full =  -1+1 = 0  消费者线程可以运行了
        sem_post(&mutex);                                           // mutex =  0+1 = 1
        sleep(1);
    }
}

/*消费者线程*/
void consumer(void *arg)
{
    int nolock = 0;
    int ret, nread;
    while(time(NULL) < end_time){
        /*P操作信号量full和mutex*/
        sem_wait(&full);                                            // full  = 0-1 = -1 阻塞等待生产者写入完成 full>=0
        sem_wait(&mutex);                                           // mutex = 1-1 = 0
        memset(buf_r, 0, sizeof(buf_r));                            // buf_r 置零
        if((nread = read(fd, buf_r, 100)) == -1){                   // 读取fd中消费者写入的数据到buf_r中
            if(errno == EAGAIN){
                printf("no data yet\n");
            }
        }
        printf("read %s from FIFO\n", buf_r);                       // 输出buf_r

        /*V操作信号量avail和mutex*/
        sem_post(&avail);                                           // avail = N
        sem_post(&mutex);                                           // mutex = 0+1 = 1
        sleep(1);
    }
}

结果分析

编译之后执行,得到了预期的结果

在这里插入图片描述

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值