2

fgetc() およびその他の入力関数は、ファイル記述子にデータがない場合に返すことができます。これは、キーボードで Ctrl-D を入力して stdin から読み取るコンソール アプリケーションでシミュレートできます (少なくとも UNIX では)。しかし、プログラムでそれを行う方法は? たとえば、次のコードでリーダー スレッドの fgetc() から戻る方法 (注意: 競合状態の可能性を無視します)。

#include <pthread.h>
#include <stdio.h>

void* reader()
{
    char read_char;
    while((read_char = fgetc(stdin)) != EOF) {
        ;
    }

    pthread_exit(NULL);
}

int main(void)
{
    pthread_t thread;
    pthread_create(&thread, NULL, reader, NULL);

    // Do something so the fgetc in the reader thread will return

    pthread_exit(NULL);
}

ありがとう!

4

5 に答える 5

3

fgetc(stdin)代わりにそのイベントを処理するために、何らかのイベントが発生したときにスレッドのブロックを停止する必要があるようです。その場合は、スレッドが両方からの入力を処理できるようにselect()、両方stdinと他のメッセージ パイプを使用できます。

fd_set descriptor_set
FD_ZERO(&descriptor_set); 
FD_SET(STDIN_FILENO, &descriptor_set); 
FD_SET(pipefd, &descriptor_set); 

if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0) 
{ 
  // select() error
} 

if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
  // read byte from stdin
  read(STDIN_FILENO, &c, 1);
}

if (FD_ISSET(pipefd, &descriptor_set)) 
  // Special event. Do something else

また、プロセス内の 1 つのスレッドのみが から読み取る必要があることに注意してくださいstdin

于 2010-11-15T15:42:35.373 に答える
1

アレクサンドルは正しい解決策を投稿しました。彼の答えは私が尋ねた質問に正確に答えます。それは彼のヒントに基づいた単純な自己コンパイルコードに従います:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>

static int pipe_fds[2];

void* user_interaction()
{
    char read_char;
    
    fd_set descriptor_set;
    FD_ZERO(&descriptor_set); 
    FD_SET(STDIN_FILENO, &descriptor_set); 
    FD_SET(pipe_fds[0], &descriptor_set);

    while(1)
    {
        if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0) {
            // select() error
        }

        if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
            // read byte from stdin
            read(STDIN_FILENO, &read_char, 1);
            // Re-set the selected file descriptor so it can
            // be signaled again
            FD_SET(STDIN_FILENO, &descriptor_set);
        }

        if (FD_ISSET(pipe_fds[0], &descriptor_set))
            // Special event. break
            break;
    }
    
    pthread_exit(NULL);
}

int main(void)
{
    pipe(pipe_fds);
    
    pthread_t thread;
    pthread_create(&thread, NULL, user_interaction, NULL);
    
    // Before closing write pipe endpoint you are supposed
    // to do something useful
    sleep(5);

    close(pipe_fds[1]);
    
    pthread_join(thread, NULL);
    
    pthread_exit(NULL);
}
于 2010-11-15T16:12:59.170 に答える
1

標準入力を「閉じる」か、標準入力を '/dev/null' (Windows では 'NUL:') にfreopen()接続するか、標準入力を '/dev/zero' に接続することができます。

閉じると、ファイル ストリームが無効になるため、すべての関数呼び出しが失敗します。null データ ソースに接続すると、すべての読み取りが失敗し、すぐに EOF が返されます。これをゼロ データ ソースに接続すると、すべての読み取りが成功し、対応する数のゼロ バイトが返されます。

それらの1つがあなたのニーズに十分に合う可能性があります. そうでない場合は、実際に何が必要かについて、より詳細な説明を提供する必要があります。

于 2010-11-15T14:57:57.963 に答える
0

この回答のコードを少し変更した形式で使用しようとしました。

void *user_interaction()
{
    char ch;
    int rv;
    fd_set set;

    FD_ZERO(&set);
    FD_SET(STDIN_FILENO, &set);
    FD_SET(pipe_fds[0], &set);

    while (1)
    {
        rv = select(pipe_fds[0] + 1, &set, NULL, NULL, NULL);
        if (rv < 0)
        {
            printf(">>> select(): error occurred, %d\n", rv);
            break;
        }
        
        if (FD_ISSET(pipe_fds[0], &set))
        {
            printf(">>> pipe_fds[0]: is ready\n");
            break;
        }

        if (FD_ISSET(STDIN_FILENO, &set))
        {
            read(STDIN_FILENO, &ch, 1);
            write(STDOUT_FILENO, &ch, 1);
        }
    }

    pthread_exit(NULL);
}

しかし、期待どおりの動作が得られませんでした。以下のように実行すると:
$ echo -n 1 | ./a.out

1私の端末は無限ループで 's でレンダリングされていましたが、パイプはselect()準備ができていると報告されませんでした (つまりclose()、メインスレッドでパイプを ing した後でも)。

FD_ZEROいくつかの実験で、必要に応じて動作させるには、 /FD_SETをループ内に移動する必要があることがわかりましたselect()

void *user_interaction()
{
    char ch;
    int rv;
    fd_set set;

    while (1)
    {
        FD_ZERO(&set);
        FD_SET(STDIN_FILENO, &set);
        FD_SET(pipe_fds[0], &set);

        rv = select(pipe_fds[0] + 1, &set, NULL, NULL, NULL);
        if (rv < 0)
        {
            printf(">>> select(): error occurred, %d\n", rv);
            break;
        }
        
        if (FD_ISSET(pipe_fds[0], &set))
        {
            printf(">>> pipe_fds[0]: is ready\n");
            break;
        }

        if (FD_ISSET(STDIN_FILENO, &set))
        {
            read(STDIN_FILENO, &ch, 1);
            write(STDOUT_FILENO, &ch, 1);
        }
    }

    pthread_exit(NULL);
}
于 2020-07-31T05:13:53.617 に答える