我有两个帖子
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);
}
}
}
答案 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
那么它是否适合使用它?
我认为这里存在一个关于互斥体性质的非常根本的误解。如果另一个线程没有同时访问该变量,那么根本不需要互斥锁
假设您在办公室做重要的工作,您需要使用复印机。一次只能有一个人使用复印机。你去复印机,有人已经在使用它。
如果你排队等候轮到你,那就是lock()
。
如果你放弃并回到你的办公桌,那就是trylock()
。 (您的程序实际上忽略了trylock()
的返回代码,因此即使其他人正在使用它,您基本上也会开始在复印机上粘贴按钮。)
现在想象一下,使用复印机需要一分钟,只有两个人使用复印机,而且他们每二十年才使用一次复印机。
如果您使用lock()
,那么在使用复印机之前,您最多等待最多一分钟。
如果您使用trylock()
,那么您会放弃并回到办公桌前等待二十年才能再次尝试复印机。
使用trylock()
没有任何意义,是吗?你的线程是否如此不耐烦,以至于他们每20年就不能花一分钟排队?
现在你的老板下来说:“我要求你复印哪份报告?”你说,“好吧,六年前我去了复印机,但有人正在使用它。”
数字(每20年一分钟)基于Latency Numbers Every Programmer Should Know,其中指出锁定/解锁互斥锁大约是25ns。因此,如果我们假装锁定然后解锁互斥锁需要一分钟,那么sleep(1)
会使线程等待二十年。