在Oracle docs for multithreading上他们有一段关于在尝试要求锁定时死锁的段落:
因为没有保证获得锁定的顺序,a 线程程序中的问题是特定的线程永远不会 获得锁定,即使它似乎应该。
当持有锁的线程释放它时,通常会发生这种情况, 让少量时间过去,然后重新获取它。因为 锁已被释放,似乎另一个线程应该获得 锁。但是,因为没有什么阻止持有锁的线程,所以 从它释放锁定到它之间继续运行 重新获取锁,因此没有其他线程运行。
为了确保我理解这一点,我试着在代码中写出来(让我知道这是否是正确的解释):
#include <mutex>
#include <chrono>
#include <thread>
#include <iostream>
std::mutex m;
void f()
{
std::unique_lock<std::mutex> lock(m); // acquire the lock
std::cout << std::this_thread::get_id() << " now has the mutex\n";
lock.unlock(); // release the lock
std::this_thread::sleep_for(std::chrono::seconds(2)); // sleep for a while
lock.lock(); // reacquire the lock
std::cout << std::this_thread::get_id() << " has the mutex after sleep\n";
}
int main()
{
std::thread(f).join(); // thread A
std::thread(f).join(); // thread B
}
那么上面的引言是说释放锁并且线程正在休眠的时间(如上面的代码)不足以保证线程等待锁获取它?这与死锁有什么关系?
答案 0 :(得分:2)
该文件正在解决一种特定的公平问题,并将其纳入关于僵局的讨论中。该文档正确地定义了死锁,意味着“永久阻塞一组线程”。然后描述了一种不太明显的实现永久阻塞条件的方法。
在所描述的场景中,假设两个线程同时尝试获取相同的锁。只有一个会赢,所以称之为 W
,另一个输了,所以称之为 L
。失败者进入睡眠状态等待轮到锁定。
引用的文字说 L
可能永远不会有机会获得锁定,即使它是由 W
发布的。
这可能发生的原因是锁不会对哪个线程获得它的公平性施加影响。锁定非常乐意让 W
永久地获取和释放它。如果 W
以释放锁定后不需要上下文切换的方式实现,那么它可能最终会在 {{1}之前再次获取锁定有机会醒来看看锁是否可用。
因此,在下面的代码中,如果L
赢得与W_thread
的初始竞争,L_thread
实际上已经陷入僵局,即使理论上它可以获得{{{}的迭代之间的锁定1}}。
L_thread
该文档建议使用W_thread
强制将上下文切换到另一个线程。此API特定于Solaris / SunOS。它的POSIX版本称为void W_thread () {
for (;;) {
std::unique_lock<std::mutex> lock(m);
//...
}
}
void L_thread () {
std::unique_lock<std::mutex> lock(m);
//...
}
,尽管某些UNIX版本(包括Linux)提供了一个名为thr_yield()
的包装器。
在C ++ 11中,这是通过std::this_thread::yield()
完成的。