Linux线程的基本使用

2023-11-09

目录

一.基本概念

二.线程

1. 线程创建

2. 线程退出

3. 线程等待

4. 线程创建Demo

三. 互斥锁

1. 创建及销毁互斥锁

2. 加锁及解锁

3. 互斥锁Demo

4.面试问题(进入死锁)

四. 条件

1. 创建及销毁条件变量

2. 等待

3. 触发

4. 条件Demo

五. 程序员检查代码小技巧


一.基本概念

典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。

父进程创建子进程,都会消耗内存(拷贝)。进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存等)的基本单位。进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。程序是静态的,进程是动态的。

"进程——资源分配的最小单位,线程——程序执行的最小单位"多进程程序要比多线程程序健壮。

多线程开发在 Linux 平台上已经有成熟的 pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。

二.线程

1. 线程创建

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);

pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程。

新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。

所有代码都是   返回:若成功返回0,否则返回错误编号

2. 线程退出

int pthread_exit(void *rval_ptr);

单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:

  1)线程只是从启动例程中返回,返回值是线程的退出码。

  2)线程可以被同一进程中的其他线程取消。

  3)线程调用pthread_exit:

ptr是一个指针。进程中的其他线程可以调用join访问。

3. 线程等待

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

调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。如果例程只是从它的启动例程返回i,如果对线程的返回值不感兴趣,可以把rval_ptr置为NULL。

4. 线程创建Demo

void *func1(void *arg)
{
        static int ret=99;                        //表示固定数据

        printf("t1 arg=%d\n",*(int *)arg);        //arg是func1的传参先(int *)转换,再取内容
        printf("t1 id=%d\n",(int)pthread_self());
        pthread_exit((void*)&ret);
}


int main ()
{
        pthread_t t1;

        int *pret=NULL;
        int arg=100;
        int ret;
        ret=pthread_create(&t1,NULL,func1,(void*)&arg);  //都是指针,先取地址,在(void*)转换
        if(ret==0){
                printf("creat yes\n");
        }
        printf("main id=%d\n",(int)pthread_self());  // 打印自己的id号

//      int pthread_join(pthread_t thread, void **rval_ptr);  
        pthread_join(t1,(void**)&pret); //先将pret取地址为二级指针,在转换为void型
        printf("pret=%d\n",*pret);
        return 0;
}

三. 互斥锁

互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。

定义全局变量

pthread_mutex_t mutex;

 1. 创建及销毁互斥锁

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t mutex);
// 返回:若成功返回0,否则返回错误编号

要用默认的属性初始化互斥量,只需把attr设置为NULL。

2. 加锁及解锁

int pthread_mutex_lock(pthread_mutex_t mutex);
int pthread_mutex_trylock(pthread_mutex_t mutex);
int pthread_mutex_unlock(pthread_mutex_t mutex);
// 返回:若成功返回0,否则返回错误编号

3. 互斥锁Demo

int g_data=0;           // 全局变量
pthread_mutex_t mutex;

void *func1(void *arg)
{
        printf("t1 arg=%d\n",*(int *)arg);
        printf("t1 id=%d\n",(int)pthread_self());
        pthread_mutex_lock(&mutex);            // 上锁
        while(1){
                printf("t1=%d\n",g_data++);
                sleep(1);
        if(g_data==3){
                printf("t1= 3 let us quit\n");
                pthread_exit(NULL);
                }
        pthread_mutex_unlock(&mutex);          //解锁
        }
}

void *func2(void *arg)
{
        printf("t2 arg=%d\n",*(int *)arg);
        printf("t2 id=%d\n",(int)pthread_self());
        while(1){
                pthread_mutex_lock(&mutex);
                printf("t2=%d\n",g_data++);
                pthread_mutex_unlock(&mutex);
                sleep(1);
        }

}
int main ()
{
        int arg=100;
        int ret;
        pthread_t *t1;
        pthread_t *t2;
        t1=(pthread_t*)malloc(sizeof(pthread_t)*128);
        t2=(pthread_t*)malloc(sizeof(pthread_t)*128);    //这里如果用指针定义的话一定要开辟 
        空间否则会出现段错误

        pthread_mutex_init(&mutex,NULL);

        ret=pthread_create(t1,NULL,func1,(void*)&arg);
        
        ret=pthread_create(t2,NULL,func2,(void*)&arg);
        
        printf("main id=%d\n",(int)pthread_self());

        pthread_join(*t1,NULL);
        pthread_join(*t2,NULL);

        pthread_mutex_destroy(&mutex);
        return 0;
}

其大致作用就是解释,被夹在pthread_mutex_lock(&mutex);和 pthread_mutex_unlock(&mutex);之间的代码不受干扰,先执行完,再去执行别的线程。这个demo的作用是想,当t1线程,g_data的值为3的时候直接调用pthread_exit(NULL);退出线程,或者是exit(0);直接退出进程.这个demo不够完善,下面介绍条件时候会有最终完善效果。

4.面试问题(进入死锁)

pthread_mutex_t mutex;
pthread_mutex_t mutex2;
int i;

void *func1(void *arg)
{
        pthread_mutex_lock(&mutex);
        sleep(1);
        pthread_mutex_lock(&mutex2);
                printf("t1 arg=%d\n",*(int *)arg);
                printf("t1 id=%d\n",(int)pthread_self());
        pthread_mutex_unlock(&mutex);
}

void *func2(void *arg)
{
        pthread_mutex_lock(&mutex2);
        sleep(1);
        pthread_mutex_lock(&mutex);
        printf("t2 arg=%d\n",*(int *)arg);
        printf("t2 id=%d\n",(int)pthread_self());
        pthread_mutex_unlock(&mutex);
}

出现在有两把锁的情况下,mutex和mutex2在两个函数。列子:锁2被线程2拿走了,线程2调用锁1的时候,线程1里面有一个锁2。一直无法被调用。

四. 条件

全局变量

pthread_cond_t cond;

1. 创建及销毁条件变量

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号

除非需要创建一个非默认属性的条件变量,否则pthread_cont_init函数的attr参数可以设置为NULL。

2. 等待

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, cond struct timespec *restrict timeout);

pthread_cond_wait等待条件变为真。如果在给定的时间内条件不能满足,那么会生成一个代表一个出错码的返回变量。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作都是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。

pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数类似,只是多了一个timeout。timeout指定了等待的时间,它是通过timespec结构指定。

3. 触发

int pthread_cond_signal(pthread_cond_t cond);
int pthread_cond_broadcast(pthread_cond_t cond);

这两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有进程。

4. 条件Demo

int g_data=0;

pthread_mutex_t mutex;

pthread_cond_t cond;

void *func1(void *arg)
{
        pthread_mutex_lock(&mutex);
        static int cnt = 0;                     //用来检测计数
        while(1){
                pthread_cond_wait(&cond,&mutex); //等待锁和条件
                sleep(1);
                printf("cond t1=%d\n",g_data);
                //      pthread_exit(NULL);
                g_data=0;                          //重置g_data为0
                sleep(1);
                if(cnt++ == 10){
                exit(1);

                }
                pthread_mutex_unlock(&mutex);       //解锁,只有完成了上述代码才能再去func2
        }
}

void *func2(void *arg)
{
        while(1){
                pthread_mutex_lock(&mutex);
                printf("t2=%d\n",g_data++);
                pthread_mutex_unlock(&mutex);

                if(g_data == 3){
                pthread_cond_signal(&cond);   //触发信号
                }
                sleep(1);
        }

}
int main ()
{
        int arg=100;
        int ret;
        pthread_t t1;
        pthread_t t2;
       
        pthread_mutex_init(&mutex,NULL);
        pthread_cond_init(&cond,NULL);   //创建条件

        ret=pthread_create(&t1,NULL,func1,(void*)&arg);
        
        ret=pthread_create(&t2,NULL,func2,(void*)&arg);
        
        pthread_join(t1,NULL);
        pthread_join(t2,NULL);

        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mutex);  //销毁条件
        return 0;
}

运行结果:

当t2线程到3的时候触发t1线程,刷新全局变量data,再次跳转到t2线程 

五. 程序员检查代码小技巧

static int cnt = 0;
if(cnt++ == 10){
                exit(1);
                }
#include <stdio.h>
int main(int argc,char **argv)
{
        int time=atoi(argv[1]);
        int i;
        for(i=0;i<time;i++){
                system("./cond");

        }
}

10为参数,将这个代码运行100遍,放到test.txt 文件之中

撒花~ 

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

Linux线程的基本使用 的相关文章

随机推荐

  • 开源DataX集成可视化项目Datax-Web的安装

    关于datax web项目 在做DataX项目测试的时候又收到github的推荐邮件 推荐了一个datax web的开源项目 这不是瞌睡遇到枕头 再研究研究这个项目是不是符合数据同步的要求 datax web https github co
  • JavaWeb 【基础】 servelet 异步操作

    在实际工作中我们会遇到一些耗时操作 这个时候如果不能及时response 就会返回失败 想要执行异步操作 延时返回数据 可以使用以下方法 使用asyncSupported true注解 WebServlet name apitest asy
  • Mysql基础(十二):隔离/锁/MVCC/ReadView

    目录 1 业务系统并发对数据库执行事务 2 脏写 脏读 3 不可重复读 4 幻读 5 事务隔离机制 6 MySQL如何支持4种隔离级别 Spring事务注解如何设置 7 undo log版本链是个什么东西 8 ReadView机制 多个事务
  • Java课题笔记~ Axios

    Axios 对原生的AJAX进行封装 简化书写 Axios官网是 https www axios http cn 2 1 基本使用 axios 使用是比较简单的 分为以下两步 引入 axios 的 js 文件 使用axios 发送请求 并获
  • Steam 灵感的游戏卡悬停效果

    先看效果 再看代码 查看更多
  • 【CSAPP】Binarybomb 实验(phase_1-6+secret_phase)

    Binarybomb 实验 phase 1 6 secret phase 实验内容 一个 binary bombs 二进制炸弹 下文将简称为炸弹 是一个Linux可执行C程序 包含了7个阶段 phase1 phase6和一个隐藏阶段 炸弹运
  • ROS学习笔记13--urdf文件报错:GazeboRosControlPlugin missing <legacyModeNS> while using DefaultRobotHWSim.....

    环境 ubuntn16 04 ROS kinetic vs code urdf文件运行时候报错如下 ERROR 1527494740 144219702 307 635000000 GazeboRosControlPlugin missin
  • jobdu 1 排序

    题目描述 对输入的n个数进行排序并输出 输入 输入的第一行包括一个整数n 1 lt n lt 100 接下来的一行包括n个整数 输出 可能有多组测试数据 对于每组数据 将排序后的n个整数输出 每个数后面都有一个空格 每组测试数据的结果占一行
  • MySQL存储过程与存储函数的区别

    语法定义上的区别就不说了 说一下其他的 如果有不正确的地方 还请大家指正 1 总述 存储函数和存储过程统称为存储例程 stored routine 两者的定义语法很相似 但却是不同的内容 存储函数限制比较多 比如不能用临时表 只能用表变量
  • STM32F4 DWT功能 实现程序运行时间精确测试

    时间戳相关寄存器定义 在Cortex M里面有一个外设叫DWT Data Watchpoint and Trace 该外设有一个32位的寄存器叫CYCCNT 它是一个向上的计数器 记录的是内核时钟运行的个数 最长能记录的时间为 10 74s
  • Dubbo各种协议

    原文地址 http dubbo io User Guide zh htm UserGuide zh 协议参考手册 协议参考手册 外链图片转存失败 源站可能有防盗链机制 建议将图片保存下来直接上传 img QStFh0Ov 166207932
  • 给定一系列正整数请按要求对数字进行分类并输出以下 5 个数字:A1=能被 5 整除的数字中所有偶数的和被5除后余1的数字按给出顺序进行交错求和被5除后余2的数字的个数被5除后余 3 的数字的平均数

    1012 数字分类 20 分 给定一系列正整数 请按要求对数字进行分类 并输出以下 5 个数字 A1 能被 5 整除的数字中所有偶数的和 A2 将被 5 除后余 1 的数字按给出顺序进行交错求和 即计算 n1 n2 n3 n4 A3 被 5
  • k8s资源控制

    文章目录 资源限定 资源限定 tomcat deploy yml apiVersion extensions v1beta1 kind Deployment metadata name tomcat deploy spec replicas
  • malloc相关内容,你知道吗?

    当一个程序经过预编译 编译 链接之后到底经历了一个怎样的过程呢 当我们写的程序内有malloc操作 那么内存到底可以申请多大呢 简单的来说下 程序首先在编译器中 经过编译之后会形成汇编格式的 o后缀的文件 经过链接之后 形成 exe的可执行
  • Java Learning - 6.3 五子棋联机模式

    在前一篇博文中完成了单机五子棋的编写 接下来将在其基础上完成联机模式 这里的联机模式采用主从形式 需要服务器端启动后 等待客户端连接以匹配游戏 目录 过程存储 胜负逻辑 悔棋 重新开始 存储本局棋谱 回放棋局 下一步 服务器类 为了进行联机
  • uni-app使用ucharts地图(主要微信小程序-初步使用)

    下载 https ext dcloud net cn plugin id 271 描述 根据数据门店数量 地图颜色逐步加深 效果 使用 目录结构 新建index vue 复制以下内容
  • 关于web3营销的一切知识

    Web3 时而神秘代表未来 有时又充满黑暗与欺骗 因为 Web3 与科技和金融紧密相关 而这两者又代表着当今世界的方向与人性 有很多人在说 Web3 就是数据的归属权转移 而我认为除此之外 Web3 更是社会里众多组织架构 利益关系 资源配
  • R语言基础 5 (常用函数及其应用)

    R语言的函数形式为 Functionname variable1 variable2 对于R中的函数可以有如下分类 下面我们将按以上分类介绍一些常见的函数 及其使用方法 一 数学函数 1 abs x 绝对值 gt abs 4 1 4 2 s
  • 低成本IMU的标定与性能评价

    低成本IMU的标定与性能评价 摘要 IMU 惯性测量单元 被广泛的使用在许多的机器人应用中 例如导航和建图任务 在大部分这种系统中 IMU和其他传感器数据 如 GPS 距离传感器 相机 进行融合 为了更好的结果 为了最小化系统的传播误差 I
  • Linux线程的基本使用

    目录 一 基本概念 二 线程 1 线程创建 2 线程退出 3 线程等待 4 线程创建Demo 三 互斥锁 1 创建及销毁互斥锁 2 加锁及解锁 3 互斥锁Demo 4 面试问题 进入死锁 四 条件 1 创建及销毁条件变量 2 等待 3 触发