信号量(Semaphore)
信号量是一个被线程共享的非负变量。信号量是一个发信号的机制。一个等待一个信号量的线程可以被其他线程通知(signal)。这个机制通过 wait 和 signal 两个原子操作(atomic operations)来实现进程同步。
一个信号量要么允许访问资源,要么不允许访问资源。二者只能选其一。而具体是哪一种,则要看设置。
详情可参考《信号量:二进位信号量和计数信号量》这篇文章。
互斥锁(Mutex)
互斥锁其实是一个对象。Mutex的全称是Mutual Exclusion Object,也就是互斥锁是一个互斥对象。它是一种特殊的二进位信号量(binary semaphore),用来控制访问共享区域资源。它包括一个优先级继承机制,以避免扩展的优先级反转问题。它允许当前优先级较高的任务在阻塞状态下维持的时间尽可能的少。然而,优先级继承并不能完全避免优先级反转,而只会最小化其影响。
信号量的使用
对于单个缓冲区(single buffer),我们可以将4kb缓冲区分成四个1kb缓冲区。信号量可以与这四个缓冲区相关联。这允许用户和生产者同时处理不同的缓冲区。
互斥锁的使用
互斥锁用于提供互斥,它使得拥有钥匙(key or mutex)的生产者才能访问资源。只要生产者占用了缓冲区(buffer),用户必须等待,反之亦然。在互斥锁的机制中,整块缓冲区始终只能提供给一个线程访问。
信号量和互斥锁的不同点
参数 | 信号量 | 互斥锁 |
---|---|---|
机制 | 是一种发信号的机制(signaling mechanism) | 是一种锁机制 |
数据类型 | 信号量是一个整型变量 | 互斥锁是一个对象 |
修改 | 等待(wait)和发信号(signal)操作可以修改信号量 | 互斥锁只有当进程请求访问一块资源或释放占用某块资源的时候被修改 |
资源管理 | 如果没有空闲资源,此时请求资源的进程将执行等待操作(wait operation)。它将一直等待直到信号量的计数大于0 | 如果互斥锁是锁住的状态,进程只能等待。进程将被置于队列中进行排队。只有当互斥锁被解锁后才能访问资源 |
线程 | 可以拥有多个线程 | 可以拥有多个线程,但是多个线程不是同时进行的 |
所有权 | 任一进程释放或者获取资源时,计数将被改变 | 锁对象只能被当前获取到钥匙的进程释放(即工作线程) |
类型 | 信号量有两种类型,二进位信号量和计数信号量 | 互斥锁没有子类型 |
操作 | 信号量的值可以通过等待和发信号两个操作来修改 | 互斥锁有锁上(locked)和解锁(unlocked)两个操作 |
资源占用 | 如果所有资源都被占用,此时请求资源的进程将执行wait()操作并阻塞自身,直到信号量计数>1,占有被释放的资源 | 如果对象已被锁定,则请求资源的进程将等待,并在释放锁定之前由系统进行排队 |
信号量的优点
下面列举信号量的优点:
- 可以多条线程(多于一条)访问重要的部分
- 信号量和机器无关
- 信号量在微内核的机器无关代码中实现
- 不允许多个进程访问重要部分
- 由于在信号量中有繁忙等待状态,因此永远不会浪费进程的时间和资源
- 是独立于机器的,应该在微内核的独立于机器的代码中运行
- 可以灵活管理资源
互斥锁的优点
下面列举互斥锁的优点:
- 互斥锁是进入关键部分时获得,接着释放掉的锁
- 由于在任何给定时刻只有一个线程处于其关键部分,因此不存在竞争的情况,并且数据始终保持一致
信号量的缺点
下面列举信号量的缺点:
- 信号量最大的限制之一是优先级反转
- 操作系统必须跟踪所有等待(wait)和发信号(signal)的调用
- 它们的使用从来没有被强制执行过,但只是按照惯例
- 为了避免信号量死锁,等待(wait)和发信号(signal)操作需要以正确的顺序执行
- 信号量编程是一种复杂的方法,所以有可能没法达到互斥
- 由于使用他们会导致了模块性的丧失,因此对于大规模的使用来说,它也不是一种实用的方法
- 信号量更容易出现程序员的编写错误
- 它可能会由于程序员编写错误而导致死锁或违反互斥
互斥锁的缺点
下面列举互斥锁的缺点:
- 如果一个线程获得一个锁并进入睡眠状态或被抢占,那么另一个线程可能无法向前移动。这可能导致饥饿
- 不能从不同于获取这个锁的上下文中锁定或解锁它
- 某个时刻只允许一个线程访问某个重要的资源
- 正常的实现可能会导致忙等待的状态,从而浪费CPU时间
网友评论