互斥体背后的想法是只允许一个线程一次访问一段内存。如果一个线程锁定互斥锁,则任何其他锁定尝试将阻塞,直到第一个锁定尝试解锁。但是,这是如何实现的?为了锁定自己,互斥锁必须设置一个位置,表示它已被锁定。但是,如果第二个互斥锁在第一个写入的同时正在读取,那该怎么办呢?更糟糕的是,如果他们同时锁定互斥锁怎么办?互斥体会屈服于它要防止的同样问题。
互斥体如何真正起作用?
答案 0 :(得分:11)
过去使用的一个简单实现是使用CPU级原子“锁定和交换”指令。这是一个特殊的指令,它将给定值与某个内存位置中的值进行原子交换。
线程可以通过尝试将1值交换到内存位置来获取此类互斥锁。如果值返回为0,则线程将假定它具有互斥锁并将继续。否则,如果返回的值为1,则线程将知道某些其他线程当前具有互斥锁。在这种情况下,它会等到再次尝试。
以上是对简单系统中可能发生的事情的高度简化概述。如今,真正的操作系统要复杂得多。
答案 1 :(得分:11)
低级原子操作。这些本质上是在硬件中实现的互斥锁,除了你只能以原子方式执行很少的操作。
考虑以下等效伪代码:
mutex global_mutex;
void InterlockedAdd(int& dest, int value) {
scoped_lock lock(mutex);
dest += value;
}
int InterlockedRead(int& src) {
scoped_lock lock(mutex);
return src;
}
void InterlockedWrite(int& dest, int value) {
scoped_lock lock(mutex);
dest = value;
}
这些函数由CPU实现为指令,它们保证线程之间的不一致程度。确切的语义取决于所讨论的CPU。 x86提供顺序一致性。这意味着操作就像按顺序按顺序发布一样。这显然涉及阻止一点。
您可以准确地推测原子操作可以用互斥体实现,反之亦然。但通常,原子操作由硬件提供,然后由操作系统在其上实现互斥和其他同步原语。这是因为有些算法不需要完整的互斥量,并且可以操作所谓的“无锁”,这意味着仅使用原子操作来实现某些线程间的一致性。
答案 2 :(得分:3)
以下是对互斥量需要工作的快速概述,它是我完整文章的缩写形式How does a mutex work?
page2
函数,它可以原子地尝试修改该值并报告它是否成功。这允许线程同时检查和修改状态。compare_and_swap
。这会将线程放在队列中,并监视内存中的整数。答案 3 :(得分:1)
你需要的只是原子地做。它可以由硬件提供,例如原子比较和交换指令,或者由操作系统通过系统调用提供。一旦它进入操作系统域,就可以很容易地确保只有一个线程试图锁定互斥锁。
在实践中,两种方法相结合。请参阅Linux的futexes。