当前位置: 首页 > backend >正文

华清远见25072班I/O学习day6

重点内容:

一、多线程基本概念

1> 线程:也称为轻量版的进程(LWP),是更小的任务执行单元,是进程的一个执行路径

2> 线程是任务器调度的最小单位,进程是资源分配的基本单位

3> 一个进程中可以包含多个线程,多个线程共享进程的资源。

4> 线程几乎不占用资源,只是占用了很少的用于线程状态的资源(大概有8k左右)

5> 由于多个线程共同使用进程的资源,导致,线程在操作上容易出现不安全的状态

6> 线程操作开销较小、任务切换效率较高

7> 一个进程中,至少要包含一个线程(主线程)

8> 在有任务执行漫长的IO等待过程中,可以同时执行其他任务

9> linux中不直接支持线程相关的支持库,需要引入第三方库,线程支持库

        sudo apt-get install manpages-posix manpages-posix-dev

如果程序中使用的线程支持库中的函数,编译程序时,需要加上 -lpthread 选项

二、多线程编程

2.1 线程的创建:pthread_create

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

        功能:创建一个分支线程,并启动该线程

        参数1:用于返回线程号的参数,传递一个pthread_t类型的变量地址,创建结束后,会得到该线程的 tid

        参数2:创建的线程属性,一般填NULL,使用系统默认线程属性

        参数3:线程体函数,是一个函数指针,参数为void*返回值也为void*

        参数4:用于主线程,向分支线程传递数据使用

        返回值:成功返回0,失败返回一个错误码(注意,不是内核提供的错误)

2.2 线程号获取:pthread_self

        pthread_t pthread_self(void);

        功能:获取当前线程的线程号

        参数:无

        返回值:返回当前线程的线程号,该函数只会成功不会失败

2.3 线程退出:pthread_exit

        void pthread_exit(void *retval);

        功能:退出该函数所在的线程

        参数:线程的退出状态,一般填NULL

        返回值:无

2.4 线程资源回收:pthread_join / pthread_detach

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

        功能:阻塞回收指定的线程结束

        参数1:要回收线程的线程号

        参数2:回收线程退出时的状态,一般填NULL

        返回值:成功返回0,失败发那会一个错误码

        int pthread_detach(pthread_t thread);

        功能:将某个线程设置成分离态,当某个线程设置成分离态后,其退出时,资源由系统回收         参数:要被设置成分离态的线程tid号

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

三、线程同步互斥

1> 竞态:同一个进程的多个线程在访问临界资源(公共资源)时,会出现抢占的现象,导致线程中的数据错误的现象称为竞态

2> 临界资源:多个线程共同访问的数据称为临界资源,可以是全局变量、堆区数据、外部文件

3> 临界区:访问临界资源的代码段称为临界区

4> 粒度:临界区的大小

5> 同步:表示多个任务有先后顺序的执行

6> 互斥:表示在访问临界区时,同一时刻只能有一个任务,当有任务在访问临界区时,其他任务只能等待

3.1 互斥机制

1> 互斥机制使用互斥锁来完成,互斥锁时一个特殊的全局变量(临界资源),该互斥锁被某一个线程获取后,其他线程想要获取该锁资源就只能等待,直到拥有该锁资源的线程释放了互斥锁。

2> 同一时刻,只能有一个线程拥有锁资源

3> 有关互斥锁的API

        1、使用pthread_mutex_t类型的变量定义一个全局变量,就有了一个互斥锁 pthread_mutex_t mutex ;

        2、初始化互斥锁 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

        功能:初始化一个互斥锁

        参数1:要被初始化的互斥锁地址

        参数2:互斥锁属性,一般填NULL,由系统自动设置

        返回值:没有失败,成功返回0

        3、获取锁资源 int pthread_mutex_lock(pthread_mutex_t *mutex);

        功能:获取指定的锁资源,如果该锁资源已经被其他线程获取,那么该函数会阻塞,直到要获取的锁资源被释放

        参数:要获取的锁资源地址

        返回值:成功返回0,失败返回非0的错误码

        4、释放锁资源 int pthread_mutex_unlock(pthread_mutex_t *mutex);

        功能:释放指定的互斥锁资源

        参数:互斥锁地址

        返回值:成功返回0,失败返回非0的错误码

        5、销毁互斥锁 int pthread_mutex_destroy(pthread_mutex_t *mutex);

        功能:销毁指定的互斥锁

        参数:互斥锁地址

        返回值:成功返回0,失败返回非0的错误码

3.2 线程同步机制

1> 线程同步机制表示要将多个线程有先后顺序的执行

2> 经典的线程同步问题:生产者消费者问题,生产者任务用于生产,通知消费者执行

3> 线程同步机制有两个:无名信号量(一个生产者对应一个消费者)、条件变量(一个生产者对应多个消费者)

3.2.1 无名信号量

1> 无名信号量本质上维护的是一个value值,当该值大于0时,表示有资源可以被申请,申请资源的函数直接获取

当value值等于0时,表示没有资源可以被获取,申请资源的函数会被阻塞

2> 无名信号量也是一个特殊的全局变量(临界资源)

3> 无名信号量相关API

        1、创建一个无名信号量:只需定义一个sem_t类型的全局变量即可 sem_t sem;

        2、初始化无名信号量  int sem_init(sem_t *sem, int pshared, unsigned int value);

        功能:初始化一个无名信号量

        参数1:无名信号量地址

        参数2:标识是进程间共享还是线程间共享 0:标识线程的同步 非0表示多个亲缘进程间同步         参数3:资源的初始值

        返回值:成功返回0,失败返回-1并置位错误码

        3、申请无名信号量资源(P操作)  int sem_wait(sem_t *sem);

        功能:申请无名信号量的资源操作,如果无名信号量中的资源大于0,则直接继续执行后面操作,如果无名信号量中的资源等于0,则该函数会阻塞 直到其他线程将该无名信号量的资源增加到大于0

        参数:无名信号量的地址

        返回值:成功返回0,失败返回-1并置位错误码

        4、释放无名信号量(V操作)int sem_post(sem_t *sem);

        功能:释放无名信号量的资源,使得无名信号量资源增加操作

        参数:无名信号量地址

        返回值:成功返回0,失败返回-1并置位错误码

        5、销毁无名信号量 int sem_destroy(sem_t *sem);

        功能:销毁一个无名信号量

        参数:无名信号量地址

        返回值:成功返回0,失败返回-1并置位错误码

3.2.2 条件变量

1> 条件变量本质上维护了一个队列,对于消费者而言,想要执行的话,先进入等待队列中进行休眠,直到生产者唤醒后继续执行

2> 对于生产者唤醒消费者的行为:先进入等待队列的消费者先执行

3> 多个消费者在进入等待队列中,会产生竞态,此时需要一个互斥锁

4> 条件变量相关API

        1、创建一个条件变量:只需要定义一个pthread_cond_t类型的全局变量,就创建了一个条件变量 pthread_cond_t cond ;

        2、初始化条件变量 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

        功能:初始化条件变量

        参数1:条件变量的地址

        参数2:条件变量的属性,一般为NULL,表示使用系统默认属性

        返回值:成功给返回0,失败返回-1并置位错误码

        3、消费者线程进入等待队列,并休眠 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

        功能:将当前线程放入等待队列中并进入休眠状态

        参数1:条件变量地址

        参数2:是一个互斥锁,表示多个线程进入阻塞队列时,是互斥进入的

        返回值:成功给返回0,失败返回-1并置位错误码

        4、唤醒等待队列中的线程函数 int pthread_cond_signal(pthread_cond_t *cond);

        功能:唤醒在等待队列中的第一个线程开始执行

        参数:条件变量地址 返回值:成功给返回0,失败返回-1并置位错误码

        唤醒等待队列中的线程函数  int pthread_cond_broadcast(pthread_cond_t *cond);

        功能:唤醒在等待队列中的所有线程

        参数:条件变量地址

        返回值:成功给返回0,失败返回-1并置位错误码 

        5、销毁条件变量 int pthread_cond_destroy(pthread_cond_t *cond);

        功能:销毁条件变量

        参数:条件变量地址

        返回值:成功给返回0,失败返回-1并置位错误码


作业:

1.创建三个线程,线程1打印A,线程2打印B,线程3打印C,使用线程同步机制,完成输出结果为:ABCABCABCABC......

程序源码:

#include <25072head.h>
//1.定义三个无名信号量
sem_t sem1,sem2,sem3;
//定义线程体1
void *task1(void *arg)
{
char buf='A';
while(1)
{
//3.申请1号无名信号量资源
sem_wait(&sem1);
write(1,&buf,sizeof(buf));
//4.释放2号无名信号量资源
sem_post(&sem2);
}
}
///定义线程体2
void *task2(void *arg)
{
char buf='B';
while(1)
{
sem_wait(&sem2);
write(1,&buf,sizeof(buf));
sem_post(&sem3);
}
}
//定义线程体3
void *task3(void *arg)
{
char buf='C';
while(1)
{
sem_wait(&sem3);
write(1,&buf,sizeof(buf));
sem_post(&sem1);
}
}

int main(int argc, const char *argv[])
{
//2.初始化无名信号量
sem_init(&sem1, 0, 1);
sem_init(&sem2, 0, 0);
sem_init(&sem3, 0, 0);

    //定义3个线程变量
pthread_t tid1,tid2,tid3;

    //创建3个线程
if(pthread_create(&tid1, NULL, task1, NULL) != 0)
{
printf("tid1 create error\n");
return -1;
}

    if(pthread_create(&tid2, NULL, task2, NULL) != 0)
{
printf("tid2 create error\n");
return -1;
}
if(pthread_create(&tid3, NULL, task3, NULL) != 0)
{
printf("tid3 create error\n");
return -1;
}

    //主线程
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);

    //5.销毁无名信号量
sem_destroy(&sem1);
sem_destroy(&sem2);
sem_destroy(&sem3);

    return 0;
}

3.思维导图:

http://www.xdnf.cn/news/20305.html

相关文章:

  • Qt绘图功能学习笔记
  • 北斗导航 | 导航定位中的卡尔曼滤波算法:原理、公式及C代码详解
  • XXL-JOB基本使用
  • MyBatis高频问题-动态sql
  • 计算机网络:以太网中的数据传输
  • golang连接influxdb的orm操作
  • halcon-亚像素边缘提取教程
  • PyTorch 模型文件介绍
  • element-plus 表单校验-表单中包含多子组件表单的校验
  • (数据结构)哈希碰撞:线性探测法 vs 拉链法
  • 基于区块链的IoMT跨医院认证系统:Python实践分析
  • Flink中的事件时间、处理时间和摄入时间
  • Joplin-解决 Node.js 中 “digital envelope routines::unsupported“ 错误
  • 自旋锁/互斥锁 设备树 iic驱动总线 day66 67 68
  • 输入2.2V~16V 最高输出20V2.5A DCDC升压芯片MT3608L
  • 计算机网络:网络设备在OSI七层模型中的工作层次和传输协议
  • 鸿蒙 BLE 蓝牙智能设备固件升级之DFU升级方式(Nordic芯片)
  • macbook intel 打开cursor会闪退
  • MySQL集群高可用架构(MHA高可用架构)
  • Process Explorer进阶(第三章3.3):深入理解进程详情
  • [Windows] AdGuard.v7.21.5089.0 中文直装电脑版
  • cds序列转换为pepperl脚本详细解读及使用
  • Python多线程编程全面指南
  • web自动化测试
  • Elasticsearch优化从入门到精通
  • 线代:排列与逆序
  • 从机器学习的角度实现 excel 中趋势线:揭秘梯度下降过程
  • PageHelper的使用及底层原理
  • WordPress如何绑定多个域名 WordPress实现多域名访问
  • 新的打卡方式