所有被空管阻塞的线程都读了

时间:2015-03-19 18:34:11

标签: c multithreading pipe fork blocking

我正在尝试自学C(Linux)中的多线程和多进程编程。我写了一个简短的程序,它生成一个新的线程,该程序进入一个例程,试图从空FIFO中进行阻塞读取,而主线程继续并打印到STDOUT。 (注意:我在执行程序之前在终端中使用mkfifo newfifo创建了一个FIFO)

我期待程序打印到屏幕“主线程”,然后在等待我将数据放入FIFO时阻塞。相反,整个过程被阻止,并且只有在我将数据放入FIFO后才打印“主线程”消息。

我在这里遗漏了什么吗?即使生成的线程被阻塞,主线程是否应该继续运行?我尝试使用fork进行测试并创建子进程并得到相同的结果(两个进程都被空FIFO读取阻塞)。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pthread.h>

#define NEWPIPE "./newfifo"

typedef struct __attribute__ ((__packed__)) {
  int reserved      :30;
  int threadStarted :1;
  int msgRcved      :1;
} Status;

void* thread_routine(int fd, char* buffer, Status* myStatus)
{
  int great_success = 0; 

  myStatus->threadStarted = 1;
  printf("Side thread\n");

  great_success = read(fd, buffer, sizeof(buffer));

  if (great_success < 0) {
    printf("pipe failed\n");
  } else {
    myStatus->msgRcved = 1;
  }
}

void main()
{
  int fd;
  int cnt = 0;
  char buffer[20];
  Status* myStatus;
  pthread_t thread_id;

  myStatus = (Status*) malloc(sizeof(Status));
  myStatus->reserved      = 0;
  myStatus->threadStarted  = 0;
  myStatus->msgRcved      = 0;

  fd = open(NEWPIPE, O_RDONLY);

  pthread_create(&thread_id, 
                  NULL, 
                  (void *) thread_routine(fd, buffer, myStatus), 
                  NULL);

  printf("Main thread\n");

  while (!myStatus->threadStarted) {
    printf("Main thread: side thread started!\n");
  }

  while (!myStatus->msgRcved) {
    sleep(1);
    cnt++;
  } 

  printf("buffer (cnt = %d): %s\n", cnt, buffer);

}

修改:最新代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pthread.h>

#define NEWPIPE "./newfifo"

struct Status {
  unsigned int reserved      :30;
  unsigned int threadStarted :1;
  unsigned int msgRcved      :1;
};

void* thread_routine(void *arg)
{
  int great_success = 0; 
  int fd;
  char buffer[20];
  struct Status* myStatus;

  fd = open(NEWPIPE, O_RDONLY);

  myStatus = arg;

  myStatus->threadStarted = 1;
  printf("Side thread\n");

  while (1) {
    great_success = read(fd, buffer, 20);

    if (great_success < 0) {
      printf("pipe failed\n");
    } else {
      printf("buffer : %s\n", buffer);
      printf("great_success = %d\n", great_success);
      great_success = 0;
    }
  }
}

void main()
{
  int cnt = 0;
  struct Status* myStatus;
  pthread_t thread_id;

  myStatus = (struct Status*) malloc(sizeof(struct Status));
  myStatus->reserved      = 0;
  myStatus->threadStarted  = 0;
  myStatus->msgRcved      = 0;

  pthread_create(&thread_id, 
                  NULL, 
                  &thread_routine,
                  (void *) myStatus);    // arguments to pass to the function!


  printf("Main thread\n");

  while (!myStatus->msgRcved) {
    printf("Main thread: side thread started!\n");

    if (myStatus->threadStarted) {
      printf("spawned thread started!\n");
    }

    sleep(1);
  }

  pthread_exit(NULL);

}

4 个答案:

答案 0 :(得分:4)

您正在将调用 thread_routine()的结果传递给pthread_create()。必须在执行调用之前对参数进行全部计算,因此在该函数返回之前不会创建该线程。大概。由于thread_routine()不返回(*)(void *),但pthread_create()尝试将返回值调用为1,因此整个程序的行为未定义。你想传递一个指向函数的指针,而不是调用它的结果:

pthread_create(&thread_id, 
              NULL, 
              thread_routine, 
              NULL);

&#34;但是这些论点怎么样,&#34;你问?这引出了我的下一点:函数thread_routine()没有线程启动例程的正确签名。线程启动例程必须接受类型为void *的单个参数。 pthread_create()的最后一个参数将作为其(单个)参数传递给指定的函数,您可以将其指向适当的struct,而不是传递多个单独的参数。

最后,您的假定线程启动函数需要通过返回指针值(可能是NULL)或通过调用pthread_exit()来退出。当main()以外的值返回函数到达其终端}而未执行return语句时,行为未定义。 (pthread_exit()解决了因为它没有返回。)

顺便说一句,请注意,您的编译器应该吐出几个关于此代码的警告。您应该始终解决所有编译器警告,或者确定为什么不安全。

答案 1 :(得分:1)

  

相反,整个过程被阻止,而消息&#34;主线程&#34;只有在我将数据放入FIFO后才会打印。

     

我在这里错过了什么吗?

您的主要帖子在此行被阻止:

fd = open(NEWPIPE, O_RDONLY);

因为FIFO的非阻塞,只读打开将阻塞,直到编写器可用。您的主线程最终被解除阻塞,而不是在您向FIFO写入数据时,而是在您打开FIFO进行写入时。

代码中存在其他问题,如@JohnBollinger discusses in his answer。但是,FIFO语义是您没有看到预期的初始输出的原因。

答案 2 :(得分:0)

the following is a way to open a named pipe, 
so there is no need for any (before running application)
processing needed.

enum enumReturnStatus create_Pipe( INT32 taskSelector  )
{
    enum    enumReturnStatus returnStatus = eRS_Success; // indicate success
    char  *pTask_NameString               = NULL;
    char    Pipe_NameString[ 100 ]        = {'\0'};
    struct  stat statInfo; // will contain info on a file 
                          // and is used to determine if the pipe already exists

    INT32 mkfifoStatus                    = 0;
    INT32 statStatus                      = 0;



    if( 0 >= Pipe_Parameters[ taskSelector ].Pipe_FD )
    { // then pipe not open

        pTask_NameString = get_pTask_NameString( taskSelector );

        if( NULL == pTask_NameString )
        { // task not configured

            return( eRS_Failure );
        }


        /* *********************************************************************
         *  implied else, task configured
         * ********************************************************************
         */


        // create pipe name string
        sprintf( Pipe_NameString, "/tmp/Pipe_2%s", pTask_NameString );



        // check if pipe already exists
        statStatus = stat(Pipe_NameString, &statInfo);

        if( (statStatus)&&(ENOENT == errno) )
        { // then, FIFO pipe does not exist

            // create the pipe
            umask(0);
            // maybe use mknode() instead of mkfifo()
            // mknod(pPipe_name, S_IFIFO | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0 );

            // 0666 allows anyone to read/write the pipe
            mkfifoStatus = mkfifo( Pipe_NameString, 0666 );

            if ( -1 == mkfifoStatus )
            {
                CommonWriteCdsLog( eLL_Critical,
                    get_pFormatString(  eFormat_CFFL_string_string ),
                    __FILE__,  __LINE__,
                    "LibFunc:mkfifo() failed to create pipe ",
                    strerror( errno ) );

fprintf(stderr,"mkfifo failed: %s \n",strerror( errno ));
fflush(stderr);

                system( "sync; sync;" );
                exit( EXIT_FAILURE );
            }
        } // endif ( pipe doesn't exist )


        if( !mkfifoStatus && !statStatus )
        { // then pipe created or already exists

            if( 0 >= Pipe_Parameters[taskSelector].Pipe_FD )
            { // then, pipe not yet open

                // note: use O_RDWR so open will not hang
                Pipe_Parameters[taskSelector].Pipe_FD = open( Pipe_NameString, O_CREAT|O_RDWR );

                if( 0 >= Pipe_Parameters[taskSelector].Pipe_FD )
                { // then open failed

                    CommonWriteCdsLog( eLL_Critical, 
                        get_pFormatString(  eFormat_CFFL_string_string ), 
                        __FILE__,  __LINE__, 
                        "LibFunc:open() failed for pipe", 
                        strerror( errno ) );
                }

                else
                { // else open successful
                    ;
                } // endif( open for read successful )
            } // endif( pipe not already open )
        } // endif( pipe created or already exists )
    }  // endif( pipe not open )

    return( returnStatus );
} // end create_Pipe()

答案 3 :(得分:-1)

此代码:

typedef struct __attribute__ ((__packed__)) {
    int reserved      :30;
    int threadStarted :1;
    int msgRcved      :1;
} Status;

会出现问题,因为代码使用了有符号值,而结构定义不应该作为typedef的类型定义:

  • 模糊了代码,
  • 在读者中产生混淆
  • 并使编译器名称空间混乱

这是定义结构的首选方法的示例 (并纠正位字段的问题)

struct status  
{
    unsigned int reserved      :30;
    unsigned int threadStarted :1;
    unsigned int msgRcved      :1;
};

不需要packed属性,因为所有位都适合单个unsigned int内存区域。通过变量定义和函数参数列表中的struct status引用此结构。