使用一个线程生成结构并使用选择器在不同的线程中接收

时间:2013-02-27 12:53:52

标签: c multithreading selector

我在一个线程中生成“event”结构,并以几秒为间隔将它们发送回主线程。当然它不能正常工作。我有两个问题:

  1. 为什么我的select循环中收到了大量事件?我是否需要从管道读取所以文件描述符不再说那里有东西?
  2. 如何从管道中提取结构的引用?
  3. 期望的输出:

    $ ...
    Writing event 1 to pipe
    Received event on pipe
    Writing event 2 to pipe
    Received event on pipe
    

    实际输出:

    $ ...
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event on pipe
    Received event o^C
    

    GCC命令:

    gcc -o test test.c
    

    test.c的:

    /* Simple example with two threads and a pipe. One thread writes structs to a 
       pipe every few seconds; the other reads the structs, prints out their info
       and then frees the structs
     */
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/select.h>
    
    void mywait(int timeInSec);
    
    // The events
    typedef struct event {
        int id;
        int data;
    } event_t;
    
    int id = 0;
    int pipefd[2];
    
    void* pump(void *arg) { // Generates events
    
        for (;;)
        {
            mywait(1); // sleep thread
    
            printf("writing event #%i to pipe\n", id);
    
            // generate event
            event_t *event;
            event = malloc(sizeof(*event));
            memset(event, 0, sizeof(*event));
            event->id = id++;
    
            // write event to pipe
            write(pipefd[1], &event, sizeof(event));
    
            // NOTE: Free event in other thread
        }
    }
    
    int main (int argc, char **argv)
    {
        printf("Starting pipe_test.c\n");
    
        // 1. Create a pipe
        pipe(pipefd);
    
        // 2. Create thread to pump events into pipe
        pthread_t tid;
        if (pthread_create( &tid, NULL, pump, NULL) != 0) {
            perror("pthread_create:pump");
            exit(-1);
        }
    
        // 3. Set up selector in main thread to read events off pipe
        int z;
        fd_set readfds; //readable file descriptor
        int selectmax;
        selectmax = pipefd[0] + 1;
        for (;;)
        {
            // Initialize selector
            FD_ZERO( &readfds );
            FD_SET( pipefd[0], &readfds );
            z = select( selectmax, &readfds, NULL, NULL, NULL );
    
            if( z < 0 ) {
                printf( "select() failed\n");
                // close( pipefd ); //???
                return 1;
            } else {
                if( FD_ISSET( pipefd[0], &readfds ) ) {
                    printf("Received event on pipe\n"); // I get a shitton of these
    
                    // Get the pointer to the event struct from the pipe
                    // TODO: GOOD WAY TO DO THIS?
    
                    // Free the struct
                    // TODO
                }
            }
        }
    
        return 0;
    }
    
    // From http://somethingswhichidintknow.blogspot.com/2009/09/sleep-in-pthread.html
    pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;
    
    void mywait(int timeInSec)
    {
        struct timespec timeToWait;
        struct timeval now;
        int rt;
        gettimeofday(&now,NULL);
    
        timeToWait.tv_sec = now.tv_sec + timeInSec;
        timeToWait.tv_nsec = now.tv_usec*1000;
    
        pthread_mutex_lock(&fakeMutex);
        rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
        pthread_mutex_unlock(&fakeMutex);
    }
    

3 个答案:

答案 0 :(得分:1)

  1. 是的,你必须从烟斗中读取,否则它将永远保持可读性。
  2. 使用read读取结构,就像使用write一样。
  3. 还有其他几点:首先是实际发送指针的地址,而不是实际的指针。其次,您不必动态分配结构,只需在堆栈上分配并发送结构。

答案 1 :(得分:1)

正如其他人所指出的,你需要实际读取数据。另请注意,您需要为整个结构分配内存,而不仅仅是指针(即event = malloc(sizeof(event_t));)。

/* Simple example with two threads and a pipe. One thread writes structs to a 
   pipe every few seconds; the other reads the structs, prints out their info
   and then frees the structs
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>

void mywait(int timeInSec);

// The events
typedef struct event {
    int id;
    int data;
} event_t;

int id = 0;
int pipefd[2];

void* pump(void *arg) { // Generates events

    for (;;)
    {
        printf("writing event #%i to pipe\n", id);

        // generate event
        event_t *event;
        event = malloc(sizeof(event_t));
        memset(event, 0, sizeof(event_t));
        event->id = id++;

        // write event to pipe
        int nbytes = write(pipefd[1], &event, sizeof(event_t*));
        //printf("written %d bytes.\n", nbytes);

        // NOTE: Free event in other thread
        mywait(1); // sleep thread
    }
}

int main (int argc, char **argv)
{
    printf("Starting pipe_test.c\n");

    // 1. Create a pipe
    pipe(pipefd);

    // 2. Create thread to pump events into pipe
    pthread_t tid;
    if (pthread_create( &tid, NULL, pump, NULL) != 0) {
        perror("pthread_create:pump");
        exit(-1);
    }

    // 3. Set up selector in main thread to read events off pipe
    int z;
    fd_set readfds; //readable file descriptor
    int selectmax;
    selectmax = pipefd[0] + 1;
    for (;;)
    {
        // Initialize selector
        FD_ZERO( &readfds );
        FD_SET( pipefd[0], &readfds );
        z = select( selectmax, &readfds, NULL, NULL, NULL );

        if( z < 0 ) {
            printf( "select() failed\n");
            // close( pipefd ); //???
            return 1;
        } else {
            if( FD_ISSET( pipefd[0], &readfds ) ) {
                printf("Received event on pipe\n"); // I get a shitton of these

                // Get the pointer to the event struct from the pipe
                event_t* received_event = NULL;
                int nbytes = read(pipefd[0], &received_event, sizeof(event_t*));
                printf("read %d bytes\n", nbytes);
                if (nbytes > 0)
                    printf("Event id: %d\n", received_event->id);

                // Free the struct
                free(received_event);
            }
        }

        mywait(1); // sleep thread
    }

    return 0;
}

// From http://somethingswhichidintknow.blogspot.com/
// 2009/09/sleep-in-pthread.html
pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;

void mywait(int timeInSec)
{
    struct timespec timeToWait;
    struct timeval now;
    int rt;
    gettimeofday(&now,NULL);

    timeToWait.tv_sec = now.tv_sec + timeInSec;
    timeToWait.tv_nsec = now.tv_usec*1000;

    pthread_mutex_lock(&fakeMutex);
    rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
    pthread_mutex_unlock(&fakeMutex);
}

答案 2 :(得分:0)

select仅告诉您有可用的数据。您必须read数据才能将其从描述符中删除。我还建议您在调试中添加一些信息(至少是一个时间戳),以便您可以看到数据创建的广度:)