我正在尝试使用O_NONBLOCK模式创建命名管道,并在单独的线程中使用“SELECT”方法侦听读取事件。当我试图在主线程中休眠一段时间后关闭程序时出现问题。我希望当使用close方法关闭命名管道的文件描述符时,select操作应该立即停止并返回一些值。但不幸的是,当关闭文件描述符并且执行select方法的线程挂起时,select操作没有反应......
任何想法如何解决?示例代码如下。
#include <pthread.h>
#include <limits.h>
#include <cctype>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <exception>
#define BUFFER PIPE_BUF
#define LPVOID void *
#define BOOL int
#define TRUE 1
#define CONST const
#define CHAR char
class CPipeTest
{
public:
int fd;
int nfd;
fd_set rfd;
pthread_t t;
CPipeTest() {};
~CPipeTest() {};
static LPVOID fnExecuteThread(LPVOID lpParam)
{
((CPipeTest*)lpParam)->fnRunThread();
return NULL;
}
BOOL fnRunThread()
{
printf("Going to listen...\r\n");
select(nfd, &rfd, NULL, NULL, NULL);
printf("Close listener...\r\n");
return TRUE;
}
void fnInit()
{
CONST CHAR * name = "./test_fifo1";
mkfifo(name, 0777);
fd = open(name, O_NONBLOCK | O_RDONLY);
nfd = fd + 1;
FD_ZERO(&rfd);
FD_SET(fd, &rfd);
pthread_create( &t, NULL, fnExecuteThread, (LPVOID)this);
sleep(30);
printf("Close file descriptor - listener should be closed automatically and immediately\r\n");
close(fd);
printf("Descriptor closed wait for thread to to be closed\r\n");
pthread_join(t, NULL);
printf("Thread is closed - everything is fine\r\n");
}
};
int main()
{
CPipeTest pi;
pi.fnInit();
return 0;
}
答案 0 :(得分:0)
尽量避免在一个线程中写入变量,并在另一个线程中读取它。
避免,我的意思是不要那样做。 :)
fnRunThread
使用的每个变量只能由该函数触及,除非您正在同步对它的访问。
在这里,你有人在做你做的事情: What does select(2) do if you close(2) a file descriptor in a separate thread? 并且指出了未定义的行为。
解决此问题的一个好方法是使用“停止读取”命令来传递你的fifo。当读取线程获取它时,它继续停止读取,然后它关闭文件。 (请注意,读取线程关闭文件句柄 - 如果读取线程正在从文件句柄读取,这意味着它拥有它。除非进行一些额外的同步,一旦启动线程,您应该只读取或写入变量由线程中的线程拥有,不应该读取或写入线程之外的这些变量。
答案 1 :(得分:0)
应该有两个文件描述符,一个用于阅读,另一个用于写入。
对于阻塞模式,在启动“reader”线程之后,应在初始线程中打开用于写入的模式(将在初始线程中关闭)。读取的那个应该在读者线程中打开。对于非阻塞模式,它可以在同一个线程中完成,如果你首先打开阅读,然后写(或者ENXIO
将返回打开没有读者的编写器)。
当您关闭书写面时,阅读方将收到select
的通知。 (如果存在真正的数据交换,则以下read
将读取零字节,这就是您检测EOF的方式。)
如果您切换到匿名管道,您将自动从pipe
电话中获得一对描述符。
答案 2 :(得分:0)
问题是FIFO(一种特殊类型的文件)必须在两个结束时打开,然后才能对它做任何事情。关闭同一端并期望其他线程做出反应是没有意义的。
以下代码应该执行您想要的操作:
....
nfd = fd + 1;
FD_ZERO(&rfd);
FD_SET(fd, &rfd);
int write_fd = open(name, O_WRONLY); // open the other end
pthread_create( &t, NULL, fnExecuteThread, (LPVOID)this);
sleep(30);
printf("Close file descriptor - listener should be closed automatically and immediately\r\n");
close(write_fd); // close the other end
....