线程的创建与同步

本文介绍了线程的概念,强调了进程与线程的区别,展示了如何使用线程相关接口函数创建和管理线程。文章通过代码示例解释了线程并发可能引发的问题,并讨论了线程同步的四种方法:信号量、互斥锁、读写锁和条件变量。此外,还提到了线程安全和fork函数在线程中的应用。
摘要由CSDN通过智能技术生成

线程的概念与实现方式

线程的概念

进程是正在执行的程序。线程是进程内部的一条执行路径,一个进程可以有多个线程。在这里插入图片描述

进程线程的区别

  • 进程是资源分配的最小单位,线程是CPU调度的最小单位。
  • 进程有自己的独立地址空间,线程共享进程中的地址空间。
  • 进程的创建消耗资源大,线程的创建相对较小。
  • 进程的切换开销大,线程的切换开销相对较小。

线程使用

线程相关的接口函数

int pthread_create(pthread_t* thread,const pthread_attr_t* attr,void* (start_routine)(void),void arg);
用于创建线程,thread:接收线程ID。attr:线程属性。start_rountine:指定线程函数。arg:线程函数传递的参数。

int pthread_exit(void *retval);
退出线程,retval:指定退出程序。

int pthread_join(pthread_t thread, void **retval);
等待thread指定的线程退出,线程未退出时,该方法阻塞。retval:接收thread线程退出时,指定的退出信息。

多线程代码

实例1:
在这里插入图片描述
运行结果:在这里插入图片描述

线程并发

在这里插入图片描述
运行结果:在这里插入图片描述
以上程序我们有五个线程,每个线程打印三次,将主程序中的i的地址传递给我们的线程,线程获取i的值将其打印。我们将三次打印的值分别分开如下图:在这里插入图片描述
我们发现三次打印的值都一样,只是顺序不一样,是因为线程运行速度不一样所以输出的顺序才会发生改变。此处要注意:例如线程5,我们将i的地址赋值给线程5时,此时i为4,但是当线程获取i的值时,i已经进入了我们第二次获取退出信息的循环,此时i=0,所以线程5才会输出0。
示例二:
在这里插入图片描述
求最后一次输出结果。
我们肯定会想到有五个进程,每个进程对count进行后置++,所以最后一次输出肯定是5000,输出结果呢大部分情况下也是5000,但是会有一部分情况下会小于5000,是为什么呢?
多个线程抢占一个cpu,就会进行并发处理,但是如果有多个处理器,就会发生一个处理器处理一个线程,导致并行处理,这样会使得两个线程同时加一,就会少一次自增。

线程的实现方式

线程的实现方式从操作系统的角度分为:用户级,内核级,组合
用户级:开销小,可以创建很多,但不能利用多处理器资源。
内核级:开销大,由内核直接管理,可以利用多处理器资源。
linux操作系统实现方式:内核级,linux比较特殊,没有特定的线程,以进程的方式实现,每个线程都有相对应的struct task_struct;PCB。

线程的同步

线程的同步指的是一个线程在对某个资源进行操作时,其他线程都不可以对这个资源进行操作,直到该线程完成操作,其他线程才可以操作,也就是协同步调,让线程按预定的先后顺序进行运行。线程同步的方法有四种:信号量,互斥锁,条件变量,读写锁

信号量

信号量特殊的资源总量,p操作获取资源-1,v操作释放资源+1。临界资源:同一时刻只允许一个进程访问的资源。临界区:访问临界资源的代码段。
头文件:#include<semaphore.h>
int sem_init(sem_t* sem,int psshared,unsigned int value);//初始化函数
int sem_wait(sem_t* sem);//p操作
int sem_post(sem_t* sem);//v操作
int sem_destroy(sem_t *sem);//销毁信号量
代码演示:
创建三个线程,让其有序输出ABCABC……
代码:
在这里插入图片描述
在这里插入图片描述
运行结果:在这里插入图片描述

互斥锁

互斥锁中有加锁和解锁操作。
pthread_mutex_init();//初始化
pthread_mutex_destroy();//销毁
pthread_mutex_lock();//加锁
pthread_mutex_unlock();//解锁
代码举例:
我们上面有一个例题,五个线程,每个线程对一个全局变量进行+1操作执行1000次,我们会发现当内核cpu大于等于2时会出现总数小于5000的情况,因为同时对同一个值进行+1操作,就等于自增一次。我们可以通过互斥锁使得这种情况进行阻塞。
在这里插入图片描述
运行结果:每一次最终相加结果为5000.在这里插入图片描述

读写锁

读写锁就和名字一样存在读和写两种操作,不同之处在于可以同时读,但是写的时候不能读,也不能写,也就是说我们进行读操作时可以进行另外的读操作,但不能进行写操作。
pthread_rwlock_rdlock//读操作
pthread_rwlock_wrlock//写操作
pthread_rwlock_unlock//解锁
pthread_rwlock_init//初始化
pthread_rwlock_destroy//删除锁
代码举例:
创建三个线程,两个进行写操作,一个进行读操作。
在这里插入图片描述
在这里插入图片描述
执行结果:在这里插入图片描述

通过运行结果我们可以发现两次读操作可以同时运行,但是写操作只能独自运行。

条件变量

条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程。在这里插入图片描述
int pthread_cond_init//初始化
int pthread_cond_wait//等待
int pthread_cond_signal//唤醒单个线程
int pthread_cond_broadcast//唤醒所有等待的线程
int pthread_cond_destroy//销毁消息变量
代码演示:
在这里插入图片描述
在这里插入图片描述

运行结果:在这里插入图片描述

线程的安全

线程安全就是在多线程运行的过程中,不管线程的调度顺序怎么样,最终结果都是安全的。
保证线程安全两点要素:

  • 对线程同步,保证同一时刻只有一个线程访问临界资源。
  • 在多线程中使用线程安全的函数。(线程安全函数:一个函数能被多个线程同时调用且巴布达省静态条件)

线程同步我们上面已经详细讲述,我们可以通过举例了解一下线程安全函数。
在这里插入图片描述
观察上面的代码,我们期待的运行结果应改时输出a1b2c3d4e…但是我们运行之后会发现结果不尽人意:
在这里插入图片描述
因为strtok函数中存在一个静态变量,导致运行两个strtok函数出现冲突。
修改后代码如下:在这里插入图片描述
运行结果如下:

在这里插入图片描述

线程与fork

fork函数在哪一个线程上仅分裂当前线程。
将线程安全代码线程中加入fork函数,
在这里插入图片描述
运行结果如下:在这里插入图片描述
我们会发现仅对当前线程进行了分裂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*闲鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值