上下文切换每次都有效,还是会导致意外行为?

时间:2016-10-27 11:09:47

标签: c++ c linux

在下面的代码中,我的朋友在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);
}

问题是,执行后它打印了陈述1,3和4 但正确的输出应该是陈述1,3和2

但是当我将func2中的睡眠值改为睡眠时(4)它工作正常。

如果我不写声明2和4,则在打印执行声明1之后,但应打印声明1和3。

稍后将func2 sleep(1)更改为sleep(4)后,将其切换回sleep(1)会变为正确的工作状态......

所有这些意外行为或是否有理由发生这种情况...... 或者纠正所有这些行为的简单解决方案将有助于我理解。 感谢。

1 个答案:

答案 0 :(得分:2)

当您生成线程时,无法保证线程将按照您生成它们的顺序开始执行。有时它会发生,有时则不会发生。我们无法保证1在3之前出现。在您致电func1pthread_create之后的任何时候,您都可以保证pthread_join在其自己的线程中被调用。直到func1返回该线程。同样适用于func2。绝对不能保证其中一个线程会在另一个线程之前开始运行。

sleep不是一个足够的同步机制。 sleep被定义为使进程等待至少 X秒。操作系统使您的进程等待10倍以上是完全合法的。不太可能,但它可能发生。特别是在繁忙的系统上。混合sleep和线程,任何事情都可能发生。由于很可能(但绝对不能保证)线程会在时间上彼此非常接近,并且系统中的定时器没有无限精度,因此很可能他们的sleep将被安排结束在“同一时间”(“同一时间”是一个非常危险的术语,但在这种情况下,它意味着一些定时器中断的相同滴答)。因此线程将被唤醒在一起,没有任何保证会在另一个之前被唤醒。无论哪一个在另一个之前被唤醒,仍然无法确定哪些将首先开始运行。你在其中一个线程之前运行的任何运气肯定会在这里运行。

你不是通过在printfs的开头而不是在结尾处使用换行来正确地刷新你的标准输出(这是从哪里来的?这么多人在stackoverflow问题中毫无理由地这样做了)。在printfs的末尾添加换行符,因为你的stdout很可能是行缓冲的,所以printfs将在行的末尾刷新。

除了最后导致死锁之外,你还有一堆没有明显功能的互斥锁。

事实上,编写代码的方式,func2开始在线程中运行,锁定l2,打印和休眠并再次打印,完全可能(尽管不太可能),锁定{{ 1}}并在任何线程开始运行l1之前返回。这会导致func1无法打印任何内容,因为它会挂起以尝试锁定func1