在下面的代码中,我的 friend 在 linux 中使用上下文切换和互斥锁模拟了死锁情况。
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t l1, l2;
void* func1();
void* func2();
int main()
{
pthread_mutex_init(&l1, NULL);
pthread_mutex_init(&l2, NULL);
pthread_t t1, t2;
pthread_create(&t1, NULL, func1, NULL);
pthread_create(&t2, NULL, func2, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
void* func1()
{
pthread_mutex_lock(&l1);
printf("\nl1 locked in func 1"); // 1
sleep(1);
printf("\ntrying to Lock l2 in func 1"); // 2
pthread_mutex_lock(&l2);
}
void* func2()
{
pthread_mutex_lock(&l2);
printf("\nl2 locked in func 2"); // 3
sleep(1);
printf("\ntrying to Lock l1 in func 2"); // 4
pthread_mutex_lock(&l1);
}
问题是,执行后它打印了 Statement 1,3 和 4 但正确的输出应该是语句 1,3 和 2
但是当我将 func2 中的 sleep 值更改为 sleep(4) 时,它工作正常。
如果我不写语句 2 和 4,在执行语句 1 后打印出来,但是应该打印语句 1 和 3。
后来把func2的sleep(1)改成sleep(4),再改回sleep(1)就变成正确的工作状态了...
所有这些意想不到的行为还是有原因的... 或者纠正所有这些行为的简单解决方案将帮助我理解。 谢谢。
最佳答案
当您生成线程时,无法保证线程会按照您生成它们的顺序开始执行。有时会发生,有时不会。不能保证 1 在 3 之前出现。唯一可以保证 func1
在调用 pthread_create
和 之后的任何时候都会在其自己的线程中调用>pthread_join
将不会返回,直到 func1
在该线程中返回。 func2
也是如此。您绝对不能保证其中一个线程会先于另一个线程开始运行。
sleep
不是一个足够的同步机制。 sleep
被定义为让进程等待至少 X 秒。操作系统让您的进程多等待 10 次是完全合法的。不太可能,但有可能发生。特别是在繁忙的系统上。混合使用 sleep
和线程,任何事情都可能发生。由于很可能(但绝对不能保证)线程彼此运行的时间非常接近,并且系统中的计时器没有无限精度,因此它们的 sleep
很可能是计划在“同一时间”结束(“同一时间”是一个非常危险的术语,但在这种情况下,它意味着某个定时器中断的同一滴答声)。所以线程将一起被唤醒,但不能保证哪个线程会先于另一个被唤醒。而且无论哪一个先于另一个被唤醒,仍然无法确定哪一个会先开始运行。如果其中一个线程先于另一个线程运行,那么运气肯定会在这里用完。
通过在 printfs 的开头而不是末尾换行,您没有正确刷新标准输出(这是从哪里来的?很多人无缘无故地在 stackoverflow 问题中这样做)。在 printfs 的末尾添加一个换行符,并且由于您的标准输出很可能是行缓冲的,因此 printfs 将在行尾刷新。
你还有一堆除了在最后导致死锁之外没有明显功能的互斥体。
事实上,代码的编写方式完全有可能(尽管不太可能)func2
开始在线程中运行,锁定 l2
,打印并休眠并且再次打印,锁定 l1
并在任何线程开始运行 func1
之前返回。这将导致 func1
不打印任何内容,因为它会在尝试锁定 l1
时挂起。
关于c++ - 上下文切换是否每次都有效,还是会导致意外行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40283110/