pthread似乎没有使用更新的全局数据值

时间:2013-04-02 13:14:23

标签: c linux multithreading pthreads

我是线程新手。我想制作两个线程xthread打印'X';并且ythread打印'Z';持续到用户在'C'处插入'c'stdin。我已经使用select来检查是否有任何用户输入。如果有用户输入,我使用scanfread中获取并进行比较。

我将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是否可以解决我的问题。我没有发现使用互斥锁的必要性。 [纠正我,如果我错了]

4 个答案:

答案 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
  • xThread读取read_input并将read_input设为1
  • yThread在我输入之前无法使用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}}不需要