0

我是线程新手。我想让两个线程xthread打印'X';并ythread打印“Z”;连续直到用户插入'C''c'stdin。我已经使用 select 来检查是否有任何用户输入。如果有用户输入,我会使用scanf它来获取它read并进行比较。

我一直保持read全球性。[有没有其他方法可以在线程之间共享非全局数据?] . 我假设,当用户'c'在 stdin 输入时,当前正在运行的线程读取它并将其存储在read and breaks out. 我已经使用该标志read_input向其他线程指示已经接受了输入并且您不需要再次接受用户输入。

问题

用户输入'c'

xthread 退出 [或 ythread]

但是,ythread 仍然保持循环并仅在我'c'再次进入后才退出。[我的假设是它已经读取了之前的值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

4 回答 4

1

有没有其他方法可以在线程之间共享非全局数据?

是的,它被称为 IPC(进程间通信)并且可以与 pthreads 一起使用。这包括:套接字、管道、共享内存等。

关于程序本身,正如Daniel Fischer 在评论中所写,read_input 不是易失性的,因此编译器可以自由地对其进行优化。

于 2013-04-02T13:22:36.687 回答
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(在我的系统上需要Return按下键)。

将该条件if (select(...))移到第二个线程之外,以便有机会退出而不select报告更多输入可用。

还,

fflush(stdin);

是未定义的行为。尽管一些实现承诺它会做一些明智的事情,但你不应该依赖它。

于 2013-04-02T14:15:01.840 回答
0

您的代码的主要问题read是不像volatile丹尼尔所说的那样。这意味着编译器不知道它可以像另一个线程一样被不可预见的外力改变。

除此之外,您的代码还有很多错误和不良做法:

  • 不要定义具有标准库名称等价物的符号,例如read.
  • 不要select在多个线程中使用相同的文件描述符。这是灾难的秘诀。如果两个线程同时从 select 返回,它们都将尝试读取,stdin并且只有一个会成功,另一个会阻塞。如果您想使用文件描述符进行同步,请将其设置为 nonblocking 和 use 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;
}
于 2013-04-02T14:15:28.900 回答
0

除了我发布的代码中的其他问题(正如其他 StackOverflow-ers 指出的那样)。我意识到,主要问题是线路/*3--->*/ if((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 1)。根据我的逻辑,如果一个线程c从用户那里读取它设置read_input为 1 的字符。并且,其他线程在访问时read_input读取更新的值(因为它是全局的)并退出 while 循环。

现在,由于我在发生此类事情的块/*2--->*/ if(!read_input)内进行检查,if/*3--->*/ if((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 1)

  • 用户输入c
  • xThread 读取c并设置read_input为 1
  • yThread 不能使用更新的值,read_input直到我在控制台上输入一些内容[即使再次按下回车键也可以]。[这表明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不需要

于 2013-04-09T13:53:42.580 回答