我是线程新手。我想制作两个线程xthread
打印'X';并且ythread
打印'Z';持续到用户在'C'
处插入'c'
或stdin
。我已经使用select来检查是否有任何用户输入。如果有用户输入,我使用scanf
在read
中获取并进行比较。
我将read
视为全球性的。 [有没有其他方法在线程之间共享非全局数据? ]。我假设,当用户在stdin输入'c'
时,当前正在运行的线程读取它并将其存储在read and breaks out
中。我已经使用标志read_input
向其他线程指示已经采用了输入,并且您不需要再次使用userinput。
问题:
用户输入'c'
xthread退出[或ythread]
然而,只有在我再次输入'c'
之后,ythread仍会保持循环并退出。
[我的假设是它已经读取了read
的先前值,并且仍然使用相同的值进行比较]
我做错了什么?
#include<stdio.h>
#include<sys/select.h>
#include<pthread.h>
static unsigned int i =0;
char read;
int read_input = 0;
void* print_fn(void* arg)
{
int fd = fileno(stdin);
struct timeval tv = {0,0};
fd_set fdset;
int s;
char *buffer = NULL;
unsigned int len;
while(1)
{
struct timespec t = {0,433300000};
const struct timespec * tp = &t;
nanosleep(tp,&t);
printf("\nValue of read is %d",read);
//sleep(1);
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
printf("\n%p prints %c and i is %d",pthread_self(),*((char*)arg),i++);
if((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 1)
{
printf("\nValue of s is %d",s);
if(!read_input)
scanf("%c",&read);
fflush(stdin);
printf("\nValue of read is %d",read);
printf("\nChecked for %d or % d",'C','c');
if(read == 'C' || read == 'c')
{
read_input = 1;
break;
}
}
printf("\nHere");
}
printf("\nI, %p survived while(1)",pthread_self());
return NULL;
}
int main()
{
pthread_t xthread,ythread,checkThread;
char c1 = 'X', c2 = 'Z';
pthread_create(&xthread,NULL,print_fn,&c1);
pthread_create(&ythread,NULL,print_fn,&c2);
pthread_join(xthread,NULL);
pthread_join(ythread,NULL);
return 0;
}
如果有更好的方式获取用户输入,请告诉我。
我不知道使用pthread_cond_t
是否可以解决我的问题。我没有发现使用互斥锁的必要性。 [纠正我,如果我错了]
答案 0 :(得分:1)
还有其他方法在线程之间共享非全局数据吗?
是的,它被称为IPC(进程间通信) 并且可以将它与pthreads一起使用。 这包括:套接字,管道,共享内存等。
关于程序本身,正如丹尼尔·菲舍尔在评论中所写,read_input不是易失性的,因此编译器可以自由地对其进行优化。
答案 1 :(得分:1)
编译器优化远离read
的读取的可能性(顺便说一句,如果有人希望#include <unistd.h>
),因为它不是volatile
,
if((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 1)
{
printf("\nValue of s is %d",s);
if(!read_input)
scanf("%c",&read);
fflush(stdin);
printf("\nValue of read is %d",read);
printf("\nChecked for %d or % d",'C','c');
if(read == 'C' || read == 'c')
{
read_input = 1;
break;
}
}
您的测试会破坏while(1)
内的if(select(...))
循环。
因此,在第一个线程读取'C'
或'c'
并退出后,另一个线程只检查stdin
(我的系统需要的新输入)时的条件返回键被按下。
将第二个帖子的if (select(...))
之外的条件移到有{}}}报告可用更多输入的情况下,有机会退出。
此外,
select
是未定义的行为。虽然有几个实现承诺它做了一些合理的事情,但你不应该依赖它。
答案 2 :(得分:0)
代码的主要问题是,read
不是volatile
,正如丹尼尔所说。这意味着编译器不知道它可以通过不可预见的外力来改变,就像另一个线程一样。
除此之外,您的代码有很多错误和不良做法:
read
。select
。这是灾难的秘诀。如果两个线程同时从select返回,则它们都将尝试从stdin
读取并且只有一个将成功,另一个将阻塞。如果你想使用文件描述符进行同步,将其设置为非阻塞并使用read
,那么它不是信号安全的,但比全面竞争条件更好。pthread.h
。i
?我没有改变它,但是__sync_fetch_and_add
会自动地做这个技巧。这是一种方式:
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
static unsigned int i =0;
volatile char char_read; // has to be volatile since you are checking it with both threads
volatile int read_input = 0; // has to be volatile
void* print_fn(void* arg)
{
// int fd = fileno(stdin);
int fd = 0; // stdin is always 0
while(1)
{
struct timespec t = {0,433300000};
const struct timespec * tp = &t;
nanosleep(tp,&t);
printf("\nValue of read is %d",char_read);
printf("\n%p prints %c and i is %d",pthread_self(),*((char*)arg),i++);
if(read_input || scanf("%c",&char_read) > 0) // attempt to read 1 byte
{
// printf("\nValue of s is %d",s);
printf("\nValue of read is %d",char_read);
printf("\nChecked for %d or % d",'C','c');
if(char_read == 'C' || char_read == 'c')
{
read_input = 1;
break;
}
}
printf("\nHere");
}
printf("\nI, %p survived while(1)\n",pthread_self());
return NULL;
}
int main()
{
// make stdin non-blocking
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
pthread_t xthread,ythread,checkThread;
char c1 = 'X', c2 = 'Z';
pthread_create(&xthread,NULL,print_fn,&c1);
pthread_create(&ythread,NULL,print_fn,&c2);
pthread_join(xthread,NULL);
pthread_join(ythread,NULL);
return 0;
}
答案 3 :(得分:0)
除了我发布的代码中的其他问题(正如其他StackOverflow-ers所指出的那样)。
我意识到,主要问题是行/*3--->*/ if((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 1)
。根据我的逻辑,如果一个线程从用户读取char c
,它将read_input
设置为1.并且,当访问read_input
时,其他线程读取更新的值(因为它是全局的) )并退出while循环。
现在,由于我正在/*2--->*/ if(!read_input)
if
/*3--->*/ if((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 1)
区内进行检查c
,会发生类似的事情,
c
read_input
并将read_input
设为1 select
的更新值
控制台上的东西[甚至点击回车键会做]
再次。 [这向if(select...block
表明某些输入可用]
我们输入read_input
并可以测试//1---->
保持原样的其余部分,
将行/*2--->*/
转移到else
,所需的/*4--->*/
/*2--->*/ if(!read_input)
{
/*3--->*/ if((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 1)
{
printf("\nValue of s is %d",s);
//1----> if(!read_input)
printf("\nValue of read is %d",read);
printf("\nChecked for %d or % d",'C','c');
if(read == 'C' || read == 'c')
{
read_input = 1;
break;
}
}
}
/*4--->*/ else
break;
printf("\nHere");
}
printf("\nI, %p survived while(1)",pthread_self());
}
就可以了。
volatile
注意: {{1}}不需要