sleep()会干扰scanf()吗?

时间:2013-04-04 14:24:42

标签: c linux pthreads sleep scanf

我有两个帖子

xThread:在控制台上连续打印X

inputThread:从stdin获取输入

当用户输入“C”或“c”

时,连续打印停止
#include<stdio.h>
#include<sys/select.h>
#include<pthread.h>
#define S sleep(0)

int read_c = 0;
pthread_mutex_t read_c_mutex = PTHREAD_MUTEX_INITIALIZER;

void* inputThread_fn(void* arg)
{
char inputChar;

while(1)
{
    S;

    printf("\nChecking input");
    scanf("%c",&inputChar);
    if(inputChar=='C' || inputChar == 'c')
    {
    pthread_mutex_trylock(&read_c_mutex); /*<--This must be _lock ?
    because with the use of trylock even If i don't aquire a lock I go ahead and modify
    the variable?*/
        read_c = 1;
    pthread_mutex_unlock(&read_c_mutex);        
    pthread_exit(NULL);
    }
}

}

void* xThread_fn(void* arg)
{
    while(1)
    {
        S;
    pthread_mutex_trylock(&read_c_mutex);
    if(!read_c)
    printf(" X");
    else
    pthread_exit(NULL);
    pthread_mutex_unlock(&read_c_mutex);
    }

}

void* yThread_fn(void* arg)
{
    while(1)
    {
        S;
    pthread_mutex_trylock(&read_c_mutex);
    if(!read_c)
    printf(" Y");
    else
    pthread_exit(NULL);
    pthread_mutex_unlock(&read_c_mutex);
    }

}


int main()
{
pthread_t xThread,yThread,inputThread;

pthread_create(&xThread,NULL,xThread_fn,NULL);
pthread_create(&inputThread,NULL,inputThread_fn,NULL);

pthread_join(xThread,NULL);
pthread_join(inputThread,NULL);

return 0;   
}

当我使用sleep(1)时,当程序到达scanf中的inputThread时,线程被生成并且[无论哪个线程首先被启动]它停止用户输入并且代码执行在我输入输入之前不要继续。

当我使用sleep(0)执行代码时,scanf不会停止输入,它会一直打印'X',直到我输入'C'或'c'

sleep()是否会干扰scanf

注意:我知道select用于非阻塞输入。我也尝试了同样的代码运行正常。我只是想在上面的例子中知道为什么会出现不一致的行为?


更新(使用trylock


 void* inputThread_fn(void* arg)
{
char inputChar;

    while(1)
    {
    S;
    scanf("%c",&inputChar);
        if(inputChar=='C' || inputChar == 'c')
        {
            pthread_mutex_trylock(&read_c_mutex);
                read_c = 1;
            pthread_mutex_unlock(&read_c_mutex);        
            pthread_exit(NULL);
        }
    }
}

void* xThread_fn(void* arg)
{
    while(1)
    {
    S;
    pthread_mutex_trylock(&read_c_mutex);
        if(!read_c)
        {
            pthread_mutex_unlock(&read_c_mutex);
            printf(" X");
        }
        else
        {
            pthread_mutex_unlock(&read_c_mutex);
            pthread_exit(NULL);
        }
    fflush(stdout); 
    }
}

void* yThread_fn(void* arg)
{
    while(1)
    {
    S;
    pthread_mutex_trylock(&read_c_mutex);
        if(!read_c)
        {
            pthread_mutex_unlock(&read_c_mutex);
            printf(" Z");
            fflush(stdout);
        }
        else
        {
            pthread_mutex_unlock(&read_c_mutex);
            pthread_exit(NULL);
        }
    }
}

1 个答案:

答案 0 :(得分:5)

您没有看到输出的原因是因为您没有刷新缓冲区。

您不需要使用sleep(0)刷新缓冲区的原因是因为编写器线程写入了大量数据,缓冲区已填满并自动刷新。

#define SLEEP_TIME 1

void* xThread_fn(void* arg)
{
    while (1) {
        sleep(SLEEP_TIME);
        pthread_mutex_lock(&read_c_mutex);
        if (read_c) {
            pthread_mutex_unlock(&read_c_mutex);
            return NULL;
        }
        pthread_mutex_unlock(&read_c_mutex);
        printf(" X");
        fflush(stdout); // <-- necessary
    }
}

不要使用pthread_mutex_trylock()

请勿在此处使用pthread_mutex_trylock()。这是错的。

lock()trylock()之间的区别在于lock()将始终成功 1 ,但trylock()有时会失败。这就是为什么它被称为“尝试”。

由于trylock()有时会失败,因此您必须处理失败的情况。你的代码没有处理这种情况:它只是向前推进,假装它获得了锁定。因此,假设trylock()没有锁定互斥锁。会发生什么?

pthread_mutex_trylock(&read_c_mutex);  // Might fail (i.e., not lock the mutex)
read_c = 1;                            // Modifying shared state (Wrong!)
pthread_mutex_unlock(&read_c_mutex);   // Unlocking a mutex (Wrong!)

然后是代码应如何处理trylock()失败的问题。如果您无法回答此问题,则默认答案为“使用lock()”。

在阅读器主题中,您无法使用trylock()因为您拥有来锁定互斥锁:

int r = pthread_mutex_trylock(&read_c_mutex);
if (r != 0) {
    // Uh... what are we supposed to do here?  Try again?
} else {
    read_c = 1;
    pthread_mutex_unlock(&read_c_mutex);
}

在作者帖子中,使用trylock()

没有意义
int r = pthread_mutex_trylock(&read_c_mutex);
if (r != 0) {
    // Okay, just try again next loop...
} else {
    if (read_c) {
        pthread_mutex_unlock(&read_c_mutex);
        pthread_exit(NULL);
    } else {
        pthread_mutex_unlock(&read_c_mutex);
    }
}

然而,这完全没有意义。写入器线程中trylock()失败的唯一原因是读者线程拥有锁定,只有在当前正在设置read_c = 1;的过程中才会发生锁定。所以你不妨等待它完成,因为你知道你将要退出(为什么在你知道用户已经发出停止的信号后会写更多输出?)

只需使用lock()即可。您将在99%的时间内使用lock(),而trylock()则用于其他1%。

1 lock()函数可能会失败,但这通常意味着您滥用了互斥锁。

lock()trylock()

的误解

你这是关于trylock()

  

如果我有另一个线程访问变量read_input那么它是否适合使用它?

我认为这里存在一个关于互斥体性质的非常根本的误解。如果另一个线程没有同时访问该变量,那么根本不需要互斥锁

假设您在办公室做重要的工作,您需要使用复印机。一次只能有一个人使用复印机。你去复印机,有人已经在使用它。

  1. 如果你排队等候轮到你,那就是lock()

  2. 如果你放弃并回到你的办公桌,那就是trylock()。 (您的程序实际上忽略了trylock()的返回代码,因此即使其他人正在使用它,您基本上也会开始在复印机上粘贴按钮。)

  3. 现在想象一下,使用复印机需要一分钟,只有两个人使用复印机,而且他们每二十年才使用一次复印机。

    1. 如果您使用lock(),那么在使用复印机之前,您最多等待最多一分钟。

    2. 如果您使用trylock(),那么您会放弃并回到办公桌前等待二十年才能再次尝试复印机。

    3. 使用trylock()没有任何意义,是吗?你的线程是否如此不耐烦,以至于他们每20年就不能花一分钟排队?

      现在你的老板下来说:“我要求你复印哪份报告?”你说,“好吧,六年前我去了复印机,但有人正在使用它。”

      数字(每20年一分钟)基于Latency Numbers Every Programmer Should Know,其中指出锁定/解锁互斥锁大约是25ns。因此,如果我们假装锁定然后解锁互斥锁需要一分钟,那么sleep(1)会使线程等待二十年。