互斥体如何真正起作用?

时间:2012-08-02 03:17:57

标签: c++ multithreading concurrency mutex

互斥体背后的想法是只允许一个线程一次访问一段内存。如果一个线程锁定互斥锁,则任何其他锁定尝试将阻塞,直到第一个锁定尝试解锁。但是,这是如何实现的?为了锁定自己,互斥锁必须设置一个位置,表示它已被锁定。但是,如果第二个互斥锁在第一个写入的同时正在读取,那该怎么办呢?更糟糕的是,如果他们同时锁定互斥锁怎么办?互斥体会屈服于它要防止的同样问题。

互斥体如何真正起作用?

4 个答案:

答案 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?

  • 内存中有一个整数表示锁定状态,值为1或0.
  • 互斥锁需要一个原子page2函数,它可以原子地尝试修改该值并报告它是否成功。这允许线程同时检查和修改状态。
  • 操作系统需要提供在互斥锁被锁定的情况下等待的功能。在Linux上,低级功能是compare_and_swap。这会将线程放在队列中,并监视内存中的整数。
  • 所涉及的操作还包括数据围栏,以防止内存修改在锁定之前可见,并在锁定后完全可用。

答案 3 :(得分:1)

你需要的只是原子地做。它可以由硬件提供,例如原子比较和交换指令,或者由操作系统通过系统调用提供。一旦它进入操作系统域,就可以很容易地确保只有一个线程试图锁定互斥锁。

在实践中,两种方法相结合。请参阅Linux的futexes。