同步与互斥:
- 同步和互斥是操作系统中多任务编程的核心概念,用于解决多个任务或进程对共享资源的访问问题
- 互斥(Mutual Exclusion) :互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排他性,互斥无法限制访问者对资源的访问顺序,即访问是无序的
- 同步(Synchronization) :同步是指在互斥的基础上,通过其它机制实现访问者对资源的有序访问,同步关注的是进程或线程之间的协作关系,确保它们按照特定的顺序执行
- 同步和同时的区别:
- 同步:指两个或多个事物在时间或状态上保持一致或协调,它强调事物之间的协调性、一致性或相互依赖关系,通常涉及某种机制来确保不同部分之间保持相同的节奏或状态
- 同时:指在某个时间点或时间段内,多个事件或行为一起发生或进行,它侧重于时间上的并列性,即多个事件在同一时刻或几乎同一时刻发生,但不一定具有关联性或协调性
- Linux中的同步与互斥:
- Linux 系统中实现同步与互斥的主要机制包括:互斥锁(Mutex)、读写锁(Read-Write Locks)、条件变量(Condition Variables)和信号量(Semaphores)
- 互斥锁(Mutex) :
- 用于线程之间的同步与互斥
- 互斥锁是一种基本的同步机制,用于确保在任意时刻只有一个线程可以访问共享资源
- 互斥锁通过加锁和解锁操作来保护临界区,防止多个线程同时进入临界区,从而避免数据不一致的问题
- 读写锁(Read-Write Locks):
- 读写锁允许多个线程同时读取共享资源,但在写入时,其他线程不能读取或写入,这种锁适用于读操作远多于写操作的场景,可以提高并发性能
- 只能有一个写线程访问资源,但是读线程可以多个同时访问
- 读操作:在读写锁的控制下,多个线程可以同时获得读锁,这些线程可以并发地读取共享资源,但它们的存在阻止了写锁的授予
- 写操作:如果至少有一个读操作持有读锁,写操作就无法获得写锁,写操作将会阻塞,直到所有的读锁都被释放
- 条件变量(Condition Variables):
- 条件变量用于在多个线程之间传递信号,通常与互斥锁一起使用,它可以在某个条件成立时唤醒等待的线程
- 条件变量可以有效地管理线程之间的等待和通知机制,避免不必要的等待和提升效率
- 信号量(Semaphores):
- 信号量通常并不是直接用来实现进程间的数据通信,而是主要用于同步和互斥,即协调进程或线程对共享资源的访问顺序,防止竞争条件发生
- 信号量本身并不传递数据,而是用于控制什么时候可以访问共享资源
互斥锁:
互斥锁:用于线程之间的同步与互斥
互斥锁是一种同步机制,用来控制对共享资源的访问,在任何时刻,最多只能有一个线程持有特定的互斥锁,如果一个线程试图获取一个已经被其他线程持有的锁,那么请求锁的线程将被阻塞,直到锁被释放
在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性,每个对象都对应于一个可称为互斥锁标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
用途:
- 保护共享数据,避免同时被多个线程访问导致的数据不一致问题
- 实现线程间的同步,确保线程之间对共享资源的访问按照预定的顺序进行
操作:
- 初始化(pthread_mutex_init):创建互斥锁并初始化(有静态和动态两种)
- 锁定(pthread_mutex_lock):获取互斥锁。如果锁已经被其他线程持有,调用线程将阻塞
- 尝试锁定(pthread_mutex_trylock):尝试获取互斥锁。如果锁已被持有,立即返回而不是阻塞
- 解锁(pthread_mutex_unlock):释放互斥锁,使其可被其他线程获取
- 销毁(pthread_mutex_destroy):清理互斥锁资源
图解:

信号量:
- 信号量(Semaphore)是一种广泛使用的同步机制,用于控制对共享资源的访问,主要在操作系统和并发编程领域中得到应用
- 信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用
- 过程:在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量,其它想进入该关键代码段的线程必须等待第一个线程释放信号量
- 简单说:信号量不是阻止并发 / 并行,而是让它们 “有序并发 / 并行”
- System V: 二值信号量、计数信号量
- POSIX:未命名信号量、命名信号量
- 目的分类:(System V)
- 二进制信号量(或称作互斥锁):其值只能是0或1,主要用于实现互斥,即一次只允许一个线程进入临界区,通常用于控制共享资源的访问,避免竞态条件的产生
- 计数信号量:其值可以是任意非负整数,表示可用资源的数量,计数信号量允许多个线程根据可用资源的数量进入临界区,通常用于控制不同进程或线程执行的顺序,如消费者必须在生产者发送数据后才可以消费
- System V:这种类型的信号量在设计上更为复杂,适合于进程间的同步,很少很少用于线程间的同步
- 名称分类:(POSIX)
- 无名信号量:无名信号量不是通过名称标识,而是直接通过 sem_t 结构的内存位置标识,无名信号量在使用前需要初始化,在不再需要时应该销毁,它们不需要像有名信号量那样进行创建和链接,因此设置起来更快,运行效率也更高
- 有名信号量:有名信号量在系统范围内是可见的,可以在任意进程之间进行通信,它们通过名字唯一标识,这使得不同的进程可以通过这个名字访问同一个信号量对象
- POSIX:可以在进程间或线程间使用,进一步分为命名信号量和未命名信号量,命名信号量可以用于不同进程间的同步,未命名信号量则适合线程间的同步。相比于 System V 信号量, POSIX 信号量的接口更简单易用,如果你是在一个现代系统上开发,特别是涉及线程或需要跨平台的兼容性, POSIX 信号量是首选,它简单、灵活,适用于大多数进程和线程同步的场景
- 无名信号量和有名信号量都可以用作二进制信号量和计数信号量
- 信号量的 PV 操作:
- P操作(Proberen,尝试):也称为等待操作(wait),用于减少信号量的值。如果信号量的值大于0,它就减1并继续执行;如果信号量的值为0,则进程或线程阻塞,直到信号量的值变为非零
- V操作(Verhogen,增加):也称为信号操作(signal),用于增加信号量的值。如果有其他进程或线程因信号量的值为0而阻塞,这个操作可能会唤醒它们