首页 文章

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

提问于
浏览
2

我正在尝试自学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);

}

Edit: Latest Code

#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 回答

  • 4

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

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

    "But what about the arguments,"你问?这引出了我的下一点:函数 thread_routine() 没有线程启动例程的正确签名 . 线程启动例程必须接受 void * 类型的单个参数 . pthread_create() 的最后一个参数将作为其(单个)参数传递给指定的函数,您可以将其指向适当的 struct ,而不是传递多个单独的参数 .

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

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

  • 1

    相反,整个过程被阻止,并且只有在我将数据放入FIFO后才打印“主线程”消息 . 我在这里错过了什么吗?

    您的主线程在此行被阻止:

    fd = open(NEWPIPE, O_RDONLY);
    

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

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

  • -1
    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()
    
  • 0

    这段代码:

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

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

    • 模糊了代码,

    • 在读者中产生混淆

    • 并使编译器名称空间混乱

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

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

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

相关问题