我正在尝试自学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 回答
您将调用
thread_routine()
的结果传递给pthread_create()
. 必须在执行调用之前对参数进行全部计算,因此在该函数返回之前不会创建该线程 . 大概 . 因为thread_routine()
不返回(*)(void *)
,但是pthread_create()
尝试将返回值调用为1,所以整个程序的行为是未定义的 . 你想传递一个指向函数的指针,而不是调用它的结果:"But what about the arguments,"你问?这引出了我的下一点:函数
thread_routine()
没有线程启动例程的正确签名 . 线程启动例程必须接受void *
类型的单个参数 .pthread_create()
的最后一个参数将作为其(单个)参数传递给指定的函数,您可以将其指向适当的struct
,而不是传递多个单独的参数 .最后,您的假定线程启动函数需要通过返回指针值(可能是
NULL
)或通过调用pthread_exit()
来退出 . 当main()
以外的值返回函数到达其终端}
而不执行return
语句时,行为未定义 . (pthread_exit()
解决了因为它没有返回 . )顺便提一下,请注意,您的编译器应该吐出几个关于此代码的警告 . 您应该始终解决所有编译器警告,或者确定为什么不安全地这样做 .
您的主线程在此行被阻止:
因为FIFO的非阻塞,只读打开将阻塞,直到写入器可用 . 您的主线程最终被解除阻塞,而不是在您将数据写入FIFO时,而是在您打开FIFO进行写入时 .
代码中存在其他问题,如@JohnBollinger discusses in his answer . 但是,FIFO语义是您没有看到预期的初始输出的原因 .
这段代码:
会出现问题,因为代码使用了有符号值,而结构定义不应该作为typedef的typedef:
模糊了代码,
在读者中产生混淆
并使编译器名称空间混乱
这是定义结构的首选方法的示例(并使用位字段更正问题)
应该不需要packed属性,因为所有位都适合单个unsigned int内存区域 . 通过变量定义和函数参数列表中的
struct status
引用此结构 .