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

2023-11-13

线程创建和退出

原理

在这里插入图片描述

代码

交叉编译

#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);
    }
}

结果分析

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

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux多线程并发运行原理+代码例程详解 的相关文章

  • 无法在 64 位 Linux 上从汇编 (yasm) 代码调用 C 标准库函数

    我有一个函数foo以汇编语言编写 并在 Linux Ubuntu 64 位上使用 yasm 和 GCC 编译 它只是使用以下命令将消息打印到标准输出puts 如下所示 bits 64 extern puts global foo secti
  • 使用 inotify 的正确方法是什么?

    我想使用inotifyLinux 上的机制 我希望我的应用程序知道文件何时aaa被改变了 您能给我提供一个如何做到这一点的示例吗 文档 来自监视文件系统活动 inotify https developer ibm com tutorials
  • 配置:错误:无法运行C编译的程序

    我正在尝试使用 Debian Wheezy 操作系统在我的 Raspberry Pi 上安装不同的软件 当我运行尝试配置软件时 我尝试安装我得到此输出 checking for C compiler default output file
  • linux下写入后崩溃

    如果我使用 write 将一些数据写入磁盘上的文件会发生什么 但我的应用程序在刷新之前崩溃了 如果没有系统故障 是否可以保证我的数据最终会刷新到磁盘 如果您正在使用write 并不是fwrite or std ostream write 那
  • 推荐用于小型站点的 IRC 服务器 (ircd)? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 情况 我想使用 IRC 机器人作为我正在研究的其他代码的通用通信接口 服务器硬件陈旧且内存不足 但运行在相对最新的 Debian GNU
  • 码头无故停止

    我需要经验丰富的码头用户的建议 我在负载均衡器 亚马逊云 后面维护着 2 台 Linux 机器 使用 Jetty 9 0 3 有时我的 Jetty 容器会被 Thread 2 无故关闭 同时地 显示以下日志并且容器无故停止 没有错误 没有例
  • 找出 Linux 上的默认语言

    有没有办法从C语言中找出Linux系统的默认语言 有 POSIX API 可以实现这个功能吗 例如 我想要一个人类可读格式的字符串 即德语系统上的 German 或 Deutsch 法语系统上的 French 或 Francais 等 有类
  • 从 Python 访问 802.11 无线管理帧

    我想从 Linux 上的 Python 嗅探 802 11 管理 探测请求 帧 这可以从 Scapy 中实现 如下所示 coding utf 8 from scapy all import def proc p if p haslayer
  • 从 php/linux 获取 pdf 的布局模式(横向或纵向)

    给定一个 PDF 如何使用 PHP lib 或 Linux 命令行工具获取 PDF 的布局模式 或相对宽度 高度 Using http www tecnick com public code cp dpage php aiocp dp tc
  • 用于编辑 /etc/sudoers 文件的正则表达式模式

    我想删除 etc sudoers 文件中的 uncommnet 轮组 那么我应该使用什么正则表达式模式 cat etc sudoers Allows members of the sys group to run networking so
  • 如何设置Java线程的CPU核心亲和力?

    我搜索了以前关于类似主题的帖子 但找不到合适的答案 因此提出这个问题 非常感谢您帮助回答 我知道在 Linux 中通过任务集命令设置进程与特定 CPU 核心的关联性 但我想设置 Java 线程与特定 cpu 核心的亲和力 以便属于同一进程的
  • 无需 root 访问权限即可安装 zsh? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 有可能 以及如何 我确实需要在几台具有 ssh 访问权限 但没有 root 访问权限 的远程计算机上使用此功能 下载 zsh wget O zsh t
  • 计算 TCP 重传次数

    我想知道在LINUX中是否有一种方法可以计算一个流中发生的TCP重传的次数 无论是在客户端还是服务器端 好像netstat s解决了我的目的
  • 在非实时操作系统/内核上执行接近实时任务的最佳方法是什么?

    在一台 GNU Linux 机器上 如果想要执行 实时 亚毫秒级时间关键 任务 您几乎总是必须经历漫长 复杂且容易出现问题的内核补丁过程 以提供足够的支持 1 http en wikipedia org wiki RTLinux Backg
  • 用于时间线数据的类似 gnuplot 的程序

    我正在寻找一个类似 gnuplot用于在时间轴中绘制数据图表的程序 类似 gnuplot 在 Linux 上运行 命令行功能 GUI 对我帮助不大 可编写脚本的语法 输出为 jpg png svg 或 gif 输出应该是这样的 set5 s
  • 如何使用 go1.6.2 构建 linux 32 位

    有没有任何组合GOARCH and GOOS我可以设置哪些值来构建 ELF 32 位二进制文 件 GOOS linux and GOARCH 386 更多示例 架构 32 bit gt GOARCH 386 64 bit gt GOARCH
  • 用于获取特定用户 ID 和进程数的 Bash 脚本

    我需要 bash 脚本来计算特定用户或所有用户的进程 我们可以输入 0 1 或更多参数 例如 myScript sh root deamon 应该像这样执行 root 92 deamon 8 2 users has total proces
  • 为什么 call_usermodehelper 大多数时候都会失败?

    从内核模块中 我尝试使用 call usermodehelper 函数来执行可执行文件 sha1 该可执行文件将文件作为参数并将文件的 SHA1 哈希和写入另一个文件 名为输出 可执行文件完美运行 int result 1 name hom
  • 在用户程序中使用 或在驱动程序模块代码中使用 ...这有关系吗?

    我正在开发一个设备驱动程序模块和关联的用户库来处理ioctl 来电 该库获取相关信息并将其放入一个结构中 该结构被传递到驱动程序模块中并在那里解压 然后进行处理 我省略了很多步骤 但这就是总体思路 一些数据通过结构体传递ioctl is u
  • 亚马逊 Linux - 安装 openjdk-debuginfo?

    我试图使用jstack在 ec2 实例上amazon linux 所以我安装了openjdk devel包裹 sudo yum install java 1 7 0 openjdk devel x86 64 但是 jstack 引发了异常j

随机推荐