互斥锁的顺序问题

6

线程锁是否按照被调用的顺序给予线程?例如,我们有三个线程A、B和C,以及一个互斥锁mtx。如果首先是A调用了mtx.lock(),然后是B和C,那么这是否意味着A将首先获取锁,接着是B和C?如果不是,那么我该怎样保证它?


3
这是一个真正的问题。[mcve]将使它成为一个_好_问题。(注意:我认为OP知道线程A确实会首先获得锁,他们问题的核心是线程B和C的锁定顺序。) - YSC
"A" 将首先获得锁,但无法保证 "B" 和 "C" 的顺序。 - AhmadWabbi
有没有一种方法可以强制执行 B 然后是 C 的顺序? - Rishi
1
@Rishi 线程可以按照先进先出的顺序注册它们的到达顺序。当一个线程醒来(例如 "C"),它会检查是否轮到它了。如果不是,它会唤醒 FIFO 队列头部的线程并再次睡眠。 - AhmadWabbi
1
重新设计系统以消除这种时间耦合,我想。 - K. Kirsz
@AhmadWabbi 我可以使用什么样的结构或类来实现这个? - Rishi
2个回答

4
如果有多个线程等待互斥锁,则会选择一个等待线程。不要假设先进先出(FIFO)的顺序。外部事件,例如内核模式APC可以更改等待顺序。-来自MSDN关于Mutex对象的页面。
请参阅链接: Mutex Objects

在这种情况下,我可以使用什么来保证顺序? - Rishi
那个链接似乎是关于某些特定平台的互斥锁 API。但没有迹象表明提问者使用除标准 C++(即 std::mutex)之外的任何内容。 - Toby Speight
当当前所有者解锁互斥量(我指的是std::mutex)时,未指定哪个线程将获得互斥量的所有权。 - Gor Asatryan

0

我有一个简单的例子来演示这个问题,我创建了3个任务,它们获取锁、打印一些内容,然后等待被加入。你可以尝试一下,将屏障设置为(2),让task_a直接运行,让另外两个任务争夺锁。

我使用boost::barrier确保它们在创建后不会立即开始执行。结果是无法确定的顺序,而不是FIFO。不同的平台会给出不同的结果。点击此处查看演示

std::mutex foo;
boost::barrier bar(3);

void task_a () {
  bar.wait();
  foo.lock();
  std::cout << "task a\n";
  foo.unlock();
}

void task_b () {
  bar.wait();
  foo.lock();
  std::cout << "task b\n";
  foo.unlock();
}

void task_c () {
  bar.wait();
  foo.lock();
  std::cout << "task c\n";
  foo.unlock();
}

int main ()
{
  std::thread th1 (task_a);
  std::thread th2 (task_b);
  std::thread th3 (task_c);
  th1.join();
  th2.join();
  th3.join();

  return 0;
}

谢谢。在上面的代码中,你如何保证FIFO顺序? - Rishi
你可以使用条件变量,在一个线程完成后,发出信号让另一个线程继续执行,以此类推到第三个线程。 - Samer Tufail
我认为条件变量也不能保证顺序,是吗?还是说我必须以某种方式实现我的代码?比如说线程th2和th3都在使用条件变量等待th1,那么th2是否保证是下一个被执行的? - Rishi
他们不会,这是方法的后半部分,你等待th1,以便执行th2。您还可以使用原子操作,在其中每个线程设置一个计数,并在设置特定计数时执行。 - Samer Tufail
还需要另一个条件变量来等待th2吗? - Rishi
好的,试一下吧。 - Samer Tufail

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接