我正在尝试自学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);
}
答案 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
引用此结构。