Linux系统编程9(线程同步,互斥锁,读写锁,条件变量和信号量)

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

Linux系统编程9(线程同步,互斥锁,读写锁,条件变量和信号量)

01. 互斥锁

1.1 同步与互斥概述

现代操作系统基本都是多任务操作系统,即同时有大量可调度实体在运行。在多任务操作系统中,同时运行的多个任务可能:

都需要访问/使用同一种资源
多个任务之间有依赖关系,某个任务的运行依赖于另一个任务

这两种情形是多任务编程中遇到的最基本的问题,也是多任务编程中的核心问题,同步和互斥就是用于解决这两个问题的。

**互斥:**是指散布在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。

**同步:**是指散布在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据。

显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个任务之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要按照某种次序来运行相应的线程(也是一种互斥)!因此互斥具有唯一性和排它性,但互斥并不限制任务的运行顺序,即任务是无序的,而同步的任务之间则有顺序关系。

1.2 为什么需要互斥锁

在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西,如果不做任何处理的话,打印出来的东西肯定是错乱的。

下面我们用程序模拟一下这个过程,线程一需要打印“ hello ”,线程二需要打印“ world ”,不加任何处理的话,打印出来的内容会错乱:

测试程序:

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

//打印字符串
void printer(char *str)
{
   
   
    while(*str != '\0')
    {
   
      
        putchar(*str);
        fflush(stdout);
        str++;
        usleep(500000);
    }   
    printf("\n");
}

//线程1执行函数
void *thread_fun1(void *arg)
{
   
   
    char *str = "hello";
    printer(str);   
}

//线程2执行函数
void *thread_fun2(void *arg)
{
   
   
    char *str = "world";
    printer(str);   
}

int main()
{
   
   
    pthread_t tid1,tid2;

    //创建线程
    pthread_create(&tid1, NULL, thread_fun1, NULL); 
    pthread_create(&tid2, NULL, thread_fun2, NULL);
    
    //等待线程结束
    pthread_join(tid1, NULL);   
    pthread_join(tid2, NULL);

    return 0;
}

运行结果:
reject
实际上,打印机是有做处理的,我在打印着的时候别人是不允许打印的,只有等我打印结束后别人才允许打印。这个过程有点类似于,把打印机放在一个房间里,给这个房间安把锁,这个锁默认是打开的。当 A 需要打印时,他先过来检查这把锁有没有锁着,没有的话就进去,同时上锁在房间里打印。而在这时,刚好 B 也需要打印,B 同样先检查锁,发现锁是锁住的,他就在门外等着。而当 A 打印结束后,他会开锁出来,这时候 B 才进去上锁打印。

1.3 互斥锁Mutex介绍

而在线程里也有这么一把锁:互斥锁(mutex),也叫互斥量,互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即加锁( lock )和解锁( unlock )。

互斥锁的操作流程如下:

1)在访问共享资源后临界区域前,对互斥锁进行加锁。

2)在访问完成后释放互斥锁导上的锁。

3)对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

互斥锁的数据类型是: pthread_mutex_t。

安装对应帮助手册:

[wiggins@localhost pthread]$ sudo yum install manpages-posix-dev

1.4 pthread_mutex_init 函数

初始化互斥锁:

#include <pthread.h>
​
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
    const pthread_mutexattr_t *restrict attr);
功能:
    初始化一个互斥锁。
参数:
    mutex:互斥锁地址。类型是 pthread_mutex_t 。
    attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。
​
    可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如:
    pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
​
这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始化,不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。
​
返回值:
    成功:0,成功申请的锁默认是打开的。
    失败:非 0 错误码

restrict,C语言中的一种类型限定符(Type Qualifiers),用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容。

1.5 pthread_mutex_destroy函数

#include <pthread.h>
​
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:
    销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非 0 错误码

1.6 pthread_mutex_lock函数

#include <pthread.h>
​
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:
    对互斥锁上锁,若互斥锁已经上锁,则调用者阻塞,直到互斥锁解锁后再上锁。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非 0 错误码
​
int pthread_mutex_trylock(pthread_mutex_t *mutex);
   调用该函数时,若互斥锁未加锁,则上锁,返回 0;
   若互斥锁已加锁,则函数直接返回失败,即 EBUSY。

1.7 pthread_mutex_unlock函数

#include <pthread.h>
​
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:
    对指定的互斥锁解锁。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非0错误码

使用互斥锁多线程打印示例程序:

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

//建立互斥锁
pthread_mutex_t mutex;
//打印
void printer(char *str)
{
   
   
    //加锁
    pthread_mutex_lock(&mutex);
    while(*str != '\0')
    {
   
      
        putchar(*str);
        fflush(stdout);
        str++;
        usleep(500000);
    
线程安全(信号量 信号量一、什么是信号量二、相关接口2.1 定义信号量:sem_t2.2 初始化信号量2.3 P操作2.4 V操作 前面我们已经说过,多线程之间自己不具备同步与互斥的功能,所以在多线程访问临界资源过程中,需要去限制线程,使之是安全的,前面我们提到了互斥的实现互斥锁同步实现条件变量,今天我们接着谈一个信号量 一、什么是信号量 信号量本质是一个计数器一个队列pcb等待队列,通过其实现进程间或者线程间的同步与互斥,关于信号量同步与互斥的实现,我在博客 进程间通信(下) 中谈到过,虽然当时讲到的是多进程 阅读详情

相关推荐

Linux线程总结:线程同步 -互斥锁条件变量信号量实现多生产者多消费者模型

学习环境 :  Centos6.5 Linux 内核 2.6 Linux线程部分总结分为两部分:(1线程的使用 ,(2线程同步与互斥。 第一部分线程的使用主要介绍,线程的概念,创建线程线程退出,以及线程的终止与分离。【完成】 地址:【Linux线程总结:初识、创建、等待、终止、分离第二部分主要介绍在多线程环境下,使用同步与互斥保护共享资源,有互斥锁条件变量信号量,以及读写锁

春风来不来的博客 3706

【C语言】sem_timedwait

函数对信号量执行减操作(通常称为wait 或 P 操作),如果信号量的值大于0,那么该函数将信号量的值减1并立即返回;不同于`sem_wait`函数,`sem_timedwait`允许你设置一个超时时间——这意味着如果指定的时间内信号量没有被释放(即其值不变为大于0),调用线程会在超时后停止阻塞,这时函数返回-1,并设置errno为ETIMEDOUT,表示已经超时。指定了一个绝对超时时间,一旦达到这个时间,如果信号量依然没有被释放(即依然为0),则函数会因超时而返回。是指向信号量对象的指针。

数字人生 4442

线程安全(互斥锁条件变量信号量读写锁)——Linux系统编程

线程安全 多个线程同时运行,访问临界资源,不会导致程序结果产生二义性,这样的情况是线程安全的。 临界资源:多线程执行流共享的资源叫临界资源。 临界区:每个线程内部访问临界资源的代码 访问:在临界区当中对临界资源进行非原子操作 原子操作:不会为任何调度机制打断的操作,该操作只有两种状态,要么完成,要么未完成。 线程不安全的原理 举个栗子:我们规定一个场景 假设现在在同一个程序当中有两个线程线程A线程B,并且有一个int类型全局变量,值为10;线程A线程B在各自的入口函数当中对这样一个变量进行++操作。

YOLO的博客 1204

系统时间变化导致sem_timedwait函数执行异常的一种解决方法

4G或5G通信模块通常是以AP+CP方式组合,AP侧运行linux系统,CP侧运行RTOS系统。一般的Linux系统系统时间可以从硬件RTC获取。RTC可以由主板电池供电,独立于系统运行。当系统关机,断电时依然维护时间。但对于有些嵌入式linxu系统,没有使用硬件RTC,系统时间通常需要linux系统完全跑起来后通过网络方式或其他方式获取。对于4G或5G通信模块,通常CP侧从网络中获取时间,AP侧再从CP侧同步时间。

四儿家的小祖宗的博客 2127

Linux使用信号量sem_timedwait当作定时器

Linux使用信号量sem_timedwait当作定时器,并与this_thread::sleep_for对比

gdizcm的专栏 2988

Linux sem_trywait实现sem_timedwait

sem_timedwait说明 int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); 其中第2个参数struct timespec *abs_timeout要求为绝对时间,如果手动将系统时间往后修改会导致sem_timedwait长时间阻塞 sem_trywait说明 int sem_trywait(sem_t *sem); sem_trywai...

xiejianjun417的专栏 2190

Linux系统编程-线程 1、创建 2线程之间共享 3、回收 4、设置线程分离 5、杀死线程 6、线程同步mutex互斥锁 7、死锁 8、rwlock读写锁9条件变量信号量 生产者消费者模型

Linux系统编程-线程 1、创建 2线程之间共享 3、回收 4、设置线程分离 5、杀死线程 6、线程同步mutex互斥锁 7、死锁 8、rwlock读写锁9条件变量信号量 生产者消费者模型

rrrrrrrrtgghyhbh的博客 123

线程编程——实现线程同步的方法(互斥锁信号量条件变量读写锁

Linux 环境中,实现线程同步的常用方法有 4 种,分别称为互斥锁信号量条件变量读写锁互斥锁(Mutex)又称互斥量或者互斥体,是最简单也最有效地一种线程同步机制。互斥锁的用法实际生活中的锁非常类似,当一个线程访问公共资源时,会及时地“锁上”该资源,阻止其它线程访问;访问结束后再进行“解锁”操作,将该资源让给其它线程访问。信号量又称“信号灯”,主要用于控制同时访问公共资源的线程数量,当线程数量控制在 ≤1 时,该信号量又称二元信号量,功能互斥锁非常类似;

主要记录嵌入式学习与开发过程。 2262

Linux线程同步机制(互斥锁读写锁条件变量信号量详细分析总结)

Linux线程同步机制(互斥锁读写锁条件变量信号量详细分析总结)

qq_42279379的博客 2209

惊!线程同步居然讲的这么详细:信号量互斥锁条件变量读写锁

1.为什么要进行线程同步操作? 我们以一个程序引出这个问题:如下: #include <stdio.h> #include<unistd.h> #include<stdlib.h> #include<pthread.h> #include<pthread.h> int val=0; void* thread_fun(void *...

李志杰的博客 5517

linux基础——linux线程间通信及同步机制总结

线程间的通信有两种情况: 1、一个进程中的线程与另外一个进程中的线程通信,由于两个线程只能访问自己所属进程的地址空间资源,故等同于进程间的通信。 2、同一个进程中的两个线程进行通信。本文说的就是第二种情况。 关于进程间通信(IPC)可以看我的另一篇博文 http://blog.csdn.net/a987073381/article/details/52006729

千里之行,始于足下 4万+

Linux 互斥锁 读写锁 条件变量 信号量

1)所谓的同步并多个线程对内存进行访问,而是按照依次进行的。2)如对线程进行同步处理,会导致线程访问出现数据混乱的问题。3)所谓就是多个线程的变量,这些变量通常为变量或者变量,这些变量对应的共享资源也被称之为。4)常用的5)通过能保证最多只能线程访问,这样就变为了。

日常解决问题的记录。 1649

Linux线程编程---线程同步互斥锁条件变量信号量读写锁

本篇博文转自http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ Linux下提供了多种方式来处理线程同步,最常用的是互斥锁条件变量信号量读写锁。  下面是思维导图: 简介 进程— 资源分配的最小单位 线程— 程序执行的最小单位 进程是一个程序的一个实例,拥有自...

君子性非异 2万+

Linux 互斥锁读写锁条件变量以及信号量

现代操作系统基本都是多任务操作系统,即同时有大量可调度实体在运行。都需要访问/使用同一种资源多个任务之间有依赖关系,某个任务的运行依赖于另一个任务这两种情形是多任务编程中遇到的最基本的问题,也是多任务编程中的核心问题,同步互斥就是用于解决这两个问题的。是指散步在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。

qq_45030264的博客 1067

Linux线程同步机制深度解析:信号量互斥锁条件变量读写锁

在多线程编程中,多个线程共享进程资源(如全局变量、文件描述符)时,若对共享资源的访问不加控制,会导致数据不一致或竞态条件。线程同步机制通过协调线程执行顺序,确保共享资源在任意时刻被正确访问。

w20010915的博客 1074

[Linux系统编程] 线程同步(互斥锁读写锁,死锁,条件变量信号量)

为简化理解,可忽略其实现细节,简单当成整数看待,只有两种取值:0,1 -------1代表未上锁,0代表已上锁。而利用条件变量,即使某线程率先抢到了锁,若条件不满足,也不能向下执行,需要先放锁给其他线程使用,提供了一种控制线程的手段。此函数作用比较复杂,一旦被调用,就会先阻塞,再释放已经持有的锁,直到该函数被其他线程唤醒后再次上锁。常用在pthread_mutex_lock上锁之后。生产者此时生产了数据后,会同时唤醒两个因条件变量阻塞的消费者,若结果A消费者拿到锁,开始消费数据,B消费者就阻塞在锁上。

SlanderMC的博客 1140

Linux下的多线程编程线程同步

Linux线程编程线程同步

PM26000的博客 1866

Linux系统编程线程同步

在多线程编程中,线程同步是确保多个线程能够安全、高效协作的关键。当多个线程访问共享资源时,如果不加以控制,可能会导致不一致的状态或错误的行为。比如:两个线程同时尝试修改同一个变量,可能导致其中一个修改被覆盖,或者产生未定义的结果。为了避免这些问题,我们需要引入同步机制来协调线程之间的操作顺序,确保每次只有一个线程可以访问特定的资源。

hope_wisdom的技术博客 1722

Linux线程() 线程同步 信号量&互斥锁&读写锁&条件变量

多个进程同时访问某些资源时,必须考虑同步问题,以确保任一时刻只有一个进程可以拥有对资源的独占式访问。通常,程序对关键资源的访问代码只是很短的一段,我们称这段代码为关键代码段或者临界区,对进程同步,也就是确保任意时刻只能有一个进程进入关键代码段。

Lyajpunov的博客 1369
上一篇: Linux系统编程8(守护进程,线程)
下一篇: 多线程打印字母
灵动的小丸子
博客等级 码龄8年 0粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值