多线程下fgets函数读取文件导致线程挂住

Deutschester 2011-08-29 07:14:26
/* 主要的参数解释:
sock——建立连接的socket
*/

void char *dQ(const int sock, const char *entry, int *flag, char* ss)
{
char *temp, *p, buf[2000];
FILE *fi;

temp = malloc(strlen(entry) + 2 + 1);
strcpy(temp, query);
strcat(temp, "\r\n");

fi = fdopen(sock, "r"); //把sock映射为对应的文件打开

if (write(sock, temp, strlen(temp)) < 0)
{
printf("my write: Bad file descriptor!\n");
*flag = 4;
free(temp);
return NULL;
}
free(temp);

while (fgets(buf, sizeof(buf), fi)) //从文件fi中逐行读入数据(每行的长度不多于1000)
{
strcat(ss, buf);
strcat(ss, "\n");
}
}

以上代码通过socket从网络服务器上请求数据。在该函数之前调用connect函数建立tcp连接,sock为连接返回的socket。
该段代码在单线程下运行正常。但在多线程(各个线程做同样的工作,会同时请求这段代码)下运行,一些线程往往会在以fgets函数为入口的内核函数挂住(阻塞住)。以下是挂住线程的调用栈:

#0 0x00007fabf8d7e4bd in read () from /lib/libc.so.6
#1 0x00007fabf8d19348 in _IO_file_underflow () from /lib/libc.so.6
#2 0x00007fabf8d1aeee in _IO_default_uflow () from /lib/libc.so.6
#3 0x00007fabf8d0f43e in _IO_getline_info () from /lib/libc.so.6
#4 0x00007fabf8d0e329 in fgets () from /lib/libc.so.6
#5 0x0000000000405c13 in dQ (sock=24, entry=0x25d2160 "split.at", flag=0x7fabf3a0adcc, ss=0x7fabf3a0ae40 "")
at ./src/whoisfunc/dQ.cpp:727
#6 ……

可见该线程在fgets阻塞住,不往下执行,不会出现异常,且不退出。而且往往是在while循环了几次后,即已从文件流fi中获取了部分文本字串,但是文件后面的内容并没有被读出却阻塞住。不知道这是什么情况,在这里卡了很长时间了,那位大神可以帮帮偶啊 多谢了!


另:该函数封装在类里面,按理讲在每个线程中使用各自的代码拷贝,并不存在共享内存的问题,为什么会出现这样只在多线程下才会出现的问题?



如下是找到的几个相关的网页,但是仍然没有解决方案:
http://www.cplusplus.com/forum/unices/7531/
http://www.gidforums.com/t-20523.html
http://www.programmersheaven.com/mb/ConLunix/385311/385311/underflow-with-fgets-and-popen/




...全文
1254 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
勤奋的沉沦 2011-08-30
  • 打赏
  • 举报
回复

The gets subroutine reads bytes from the standard input stream, stdin, into the array pointed to by the String parameter. It reads data until it reaches a new-line character or an end-of-
file condition. If a new-line character stops the reading process, the gets subroutine discards the new-line character and terminates the string with a null character.

The fgets subroutine reads bytes from the data pointed to by the Stream parameter into the array pointed to by the String parameter. The fgets subroutine reads data up to the number of bytes
specified by the Number parameter minus 1, or until it reads a new-line character and transfers that character to the String parameter, or until it encounters an end-of-file condition. The
fgets subroutine then terminates the data string with a null character.

The first successful run of the fgetc (getc, getchar, fgetc, or getw Subroutine), fgets, fgetwc (getwc, fgetwc, or getwchar Subroutine), fgetws (getws or fgetws Subroutine), fread (fread or
fwrite Subroutine), fscanf, getc (getc, getchar, fgetc, or getw Subroutine), getchar (getc, getchar, fgetc, or getw Subroutine), gets or scanf subroutine using a stream that returns data not
supplied by a prior call to the ungetc or ungetwc subroutine marks the st_atime field for update.

上面是fgets的说明。
建议不要使用fgets这样的函数读取网络数据。使用read读完数据,然后丢给消费线程处理。
louyong0571 2011-08-30
  • 打赏
  • 举报
回复
while循环里,fget失败的时候是不是应该要延时一会儿会好点?
Deutschester 2011-08-30
  • 打赏
  • 举报
回复

使用了selecet来监视sock文件(事先设置为非阻塞读取方式)的可读状态,在超时时间内可读时再使用fgets读取。超时的话,则退出函数,从而不会让线程挂起(给人以线程死锁的假象)。这样做了以后,基本就达到了软件设计的需求了。谢谢大家的帮助!


现在函数修改为:

void char *dQ(const int sock, const char *entry, int *flag, char* ss)
{
char *temp, *p, buf[2000];
FILE *fi;

temp = malloc(strlen(entry) + 2 + 1);
strcpy(temp, query);
strcat(temp, "\r\n");

/*将sock描述文件设置为非阻塞方式*/
int flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags|O_NONBLOCK);

fi = fdopen(sock, "r"); //把sock映射为对应的文件打开

if (write(sock, temp, strlen(temp)) < 0)
{
printf("my write: Bad file descriptor!\n");
*flag = 4;
free(temp);
return NULL;
}
free(temp);
fd_set rfd;
struct timeval timeout;
timeout.tv_sec = FGETSTIMEOUT ; //指定fgets函数的超时 时间为FGETSTIMEOUT(自定义宏)
timeout.tv_usec = 0;

while(1)
//while (fgets(buf, sizeof(buf), fi)) //从文件fi中逐行读入数据(每行的长度不多于1000)
{
FD_ZERO(&rfd);
FD_SET(sock, &rfd);
int res = select(sock+1, &rfd, NULL, NULL, &timeout);
if(res < 0) {
printf("select error!\n");
*flag = 5; //用作他用,无关
return NULL;
}
else if(res == 0) {
printf("select timeout!\n");
*flag = 5; //用作他用,无关
return NULL;
}
else{
if(FD_ISSET(sock, &rfd)){
if(NULL == fgets(buf, sizeof(buf) - 1, fi))
break;
}
}

strcat(ss, buf);
strcat(ss, "\n");
}
}
念茜 2011-08-29
  • 打赏
  • 举报
回复
lz可以换用多路复用的思想试试,一个读取线程select多个描述符,当有可读时,fgets得到数据然后分给多个处理线程。
库函数考虑更多线程安全问题,io的buff考虑更多同步的问题,他们在这里可能有冲突造成阻塞。
justkk 2011-08-29
  • 打赏
  • 举报
回复
会不会与标准输入输出的缓冲有关?
试试不用FILE *的方式读,直接用read来读
Deutschester 2011-08-29
  • 打赏
  • 举报
回复
问题补充:一般情况下多个线程是可以正常工作的,但是过些时间少数线程会不定期出现上面描述的挂住(但是已经获得了部分的数据,线程就阻塞在等待获取剩余的数据上了)的情形。有时甚至不会出现以上描述的挂住的情形。

由于fi是一个从网络socket映射过来的文件,因此我推测由于网络连接错误(超时、重置等)、系统IO缓存共享、请求网络数据阻塞等都可能是引起这个问题的原因,由于对这些了解不够,请各位多多指教……
jackyjkchen 2011-08-29
  • 打赏
  • 举报
回复
socket独立应该没问题的

你单线程确定没问题?(socket接收的内容要保证是字符串)

你换fread试试看行不行

Deutschester 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 jackyjkchen 的回复:]

你是多个线程同时fgets?没必要吧
[/Quote]

不是连向同一个服务器的。使用多线程建立多个连向不同服务器的连接,每个连接请求获得不同的数据。这样做的目的是为了并发访问不同服务器,在单位时间获得更多的数据……
jackyjkchen 2011-08-29
  • 打赏
  • 举报
回复
你是多个线程同时fgets?没必要吧
代码下载地址: https://pan.quark.cn/s/068306a56640 在交通运输科学范畴内,交通流量分配是探讨交通系统内交通负荷如何在各个道路路段间进行合理分配的核心议题。交通流分配技术主要应用于模拟和推演交通系统的运行状态,旨在为城市规划人员与交通工程师提供优化道路布局的参考,进而缓解交通拥堵现象,提升道路交通的整体效能。以下将对"交通流分配技术"进行深入阐述: 1. **全部或无分配(All-or-Nothing Distribution)** 全部或无分配是一种基础的交通流分配策略。该技术依据最短路径准则,预设所有从发源地(Origin)至目的地(Destination)的车辆都将选择路程最短的路线。倘若存在多条等长路径,则采用随机方式确定其中一条。此类方法未将交通拥堵因素纳入考量,因为一旦选定路径,便假定所有交通量将沿此路径通行,即便这可能导致部分路段出现超负荷状况。 2. **逐步分配(Progressive Assignment)** 逐步分配技术顾及了交通网络的动态特征。该算法通过逐步优化流量配置,力求使网络内行驶的总代价(诸如通行时间、距离等指标)最小化。运算过程始于初始的全部或无分配状态,随后逐步修正路径选择,直至网络达成稳定状态,即每次流量修正后,总代价的变动幅度不再明显。此方法更贴近现实交通场景,因为车辆会依据即时路况选择最优路径。 3. **迭代平均分配(Iterative Average Distribution)** 迭代平均分配是一种对逐步分配技术的优化版本,其更侧重于实现网络流量的均衡化。在每一轮迭代过程中,算法不仅评估当前路径的优化程度,同时参考前一次迭代的流量配置。通过融入平均化理念,该方法能更有效地分散交通...

23,223

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧