互斥锁优先级

5
在多线程(2个线程)程序中,我有以下代码:
while(-1)
{
    m.lock();

    (...)

    m.unlock();
}

m是一个互斥锁(在我的情况下是一个C++11的std::mutex,但我认为如果使用不同的库,结果不会改变)。

假设第一个线程拥有互斥锁并且在(...)部分执行了一些操作。第二个线程试图获取互斥锁,但它正在等待第一个线程释放m

问题是:当线程1结束它的(...)执行并释放了互斥锁时,我们能否确定线程2会获取互斥锁,还是线程1可以在线程2之前重新获取互斥锁,使其被卡在lock()中?


OT:如上所述,应该使用作用域锁定,而不是手动锁定和解锁。否则,while循环中抛出的异常将挂起互斥量(除非您指望这种情况发生)。 - WhozCraig
我只花了几天时间来进行多线程编程,所以对此并不是很专业,但是你的逻辑/代码存在至少两个问题:1)没有使用RAII范式;2)应该避免使用用户管理的互斥锁。例如,boost::thread提供了基于作用域的互斥锁,相比起这段代码来说更好,如果你问我的话。 - user1849534
5个回答

4

C++标准没有保证互斥锁的获取顺序。因此,活动线程完全有可能在不尝试获取锁的其他线程之前一直执行unlock()lock()操作std::mutex m。我认为C++标准没有提供控制线程优先级的方法。我不知道你想做什么,但可能有另一种方法可以避免你遇到的问题。


我认为现在在实际应用中使用C++11线程特性还为时过早,最新的编译器如gcc和clang仍然没有很好的支持,总的来说,我发现使用boost::thread比使用C++11的这一套特性更好。 - user1849534

3
如果两个线程的优先级相同,标准互斥锁实现就不能保证这一点。一些操作系统有“等待队列”,在释放某些东西时会选择“最长等待”的那个线程,但这是一个实现细节,不是你可以可靠依赖的东西。
想象一下,你有两个线程,每个线程运行类似于以下内容:
m.lock();

(...)

m.unlock();
(...)    // Clearly not the same code as above (...)
m.lock();

(...)    // Some other code that needs locking against updates. 

m.unlock();

你希望上述代码在第二个锁上每次都切换线程吗?

顺便说一下,如果两个线程在整个循环中都使用锁,那么使用锁的意义是什么?


实际上,我简化了问题:第二个线程没有那个结构,它等待外部事件,当它发生时,我需要第一个线程完成(...)执行(在lock()-unlock()内),并等待直到线程2结束其执行,接下来线程1可以继续其无限循环。两个线程共享一些变量。 因此,如果我将第二个线程的优先级设置为比第一个线程更高,它会起作用吗? - ocirocir
更高的优先级可能会有所帮助,但要考虑这样一种情况:当第二个处理器存在且正在等待的线程刚刚在发现锁是空闲的时候才被启动 - 但是第一个已经在运行的线程在到达互斥锁之前只有一个跳转指令,因此它“赢得了”比赛。如果您需要排序,则需要更复杂的锁结构。 - Mats Petersson

1

没有任何保证,因为线程之间没有任何顺序。实际上,唯一的同步点就是互斥锁。

如果第一个线程在紧密循环中运行函数,那么它完全有可能立即重新获取锁。典型的实现方式是,在互斥锁上有一个通知和唤醒机制,如果任何线程正在等待互斥锁,则会被唤醒。但也可能存在让正在运行的线程继续而不进行上下文切换的偏向...这非常取决于实现和平台的细节。


1

C++或底层操作系统并不提供任何保证。

然而,线程到达关键区域(在本例中为互斥锁)的时间会有一定的公平性。这种公平性可以表示为统计概率,但并非严格保证。最有可能的情况是由操作系统执行调度器决定,该调度器还将考虑许多其他因素。


-1

依赖这样的代码并不是一个好主意,所以你应该考虑改变你的设计。 然而,在一些操作系统上,sleep(0)会让线程让出CPU。(在Windows上是Sleep(0)) 再次强调,最好不要依赖这个。


1
sleep / yield或类似的函数仅保证调度程序运行。如果当前线程是多处理器系统上唯一可运行的线程,则第一个线程完全有可能在另一个线程到达之前“锁定”。 - Mats Petersson

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