免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 21506 | 回复: 77
打印 上一主题 下一主题

[C] 多线程服务器,发现个问题,请高人解惑 [复制链接]

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-07-05 12:05 |只看该作者 |倒序浏览
本帖最后由 yulihua49 于 2010-07-05 12:09 编辑

怪现象:当客户端密集到达连接时,accept竟然会返回重复的s(socket),导致后边若干线程使用相同的socket,通信乱套了,内存也乱套了。
为此在成功create_thread后,特意usleep(10000);有改善,没根本解决。
  1.        。。。。前边正常,略
  2. listen(sock,1000);
  3.         while(1) {
  4.                 do {
  5.                         FD_ZERO(&efds);
  6.                         FD_SET(sock, &efds);
  7. //健康检查周期5分钟
  8.                         tm.tv_sec=300;
  9.                         tm.tv_usec=0;
  10.                         ret=select(sock+1,&efds,NULL,&efds,&tm);
  11.                         if(ret==-1) {
  12.                                 ShowLog(1,"select error %s",strerror(errno));
  13.                                 close(sock);
  14.                                 quit(3);
  15.                         }
  16.                         if(ret==0 && poolchk) poolchk();
  17.                 } while(ret<=0);
  18.                 s=accept(sock,(struct sockaddr *)&cin,&leng);//就是返回的s有问题了。
  19.                 if(s<0) {
  20.                         ShowLog(1,"%s:accept err=%d,%s",__FUNCTION__,errno,strerror(errno));
  21.                         switch(errno) {
  22.                         case EMFILE:    //fd用完了,其他线程还要继续工作,主线程休息一下。
  23.                         case ENFILE:
  24.                                 sleep(60);
  25.                                 continue;
  26.                         default:break;
  27.                         }
  28.                         sleep(3);
  29.                         if(++repeat < 20) continue;
  30.                         ShowLog(1,"%s:network fail! err=%s",__FUNCTION__,strerror(errno));
  31.                         close(sock);
  32.                         quit(5);
  33.                 }
  34.                 Conn.Socket=s;
  35.                 Conn.only_do=conn_init; //借用一下
  36.                 Conn.SendLen=sizeof_gda;
  37.                 ret=pthread_create(&pthread_id,&attr,thread_work,&Conn);
  38.                 if(ret) {
  39.                         ShowLog(1,"%s:pthread_create:%s",__FUNCTION__,strerror(ret));
  40.                         close(s);
  41.                         if(ret==EAGAIN||ret==ENOMEM) {  //线程数用完了,休息一会,等一些线程退出
  42.                                 sleep(30);
  43.                         }
  44.                         continue;
  45.                 }
  46.                 usleep(10000);//歇会
  47.         }

  48.         close(sock);
  49.         quit(0);
  50. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2010-07-05 12:23 |只看该作者
本帖最后由 lenky0401 于 2010-07-05 12:38 编辑

select尽量不要和多并发thread用吧,nginx代码里就禁止这么使用。至于原因嘛,我也不知道,只是前些天看到nginx代码是遇到这种情况就直接返回ERR了,当时就有疑问为什么会禁止这种组合使用,但没查到什么原因,但猜想可能与线程安全有关?刚才又通过关键字“select thread-safe”google了一把,网上的讨论也有,比如:
http://bugs.python.org/issue8865
http://stackoverflow.com/questio ... -how-to-work-around
http://www.qnx.com/developers/do ... b_ref/s/select.html

Caveats:
The select() function only works with raw file descriptors; it doesn't work with file descriptors in edited mode. See the ICANON flag in the description of the tcgetattr() function.

The select() function is thread safe as long as the fd sets used by each thread point to memory that is specific to that thread.

In Neutrino, if multiple threads block in select() on the same fd for the same condition, all threads may unblock when the condition is satisfied. This may differ from other implementations where only one thread may unblock.


如上,为了避免出现乱七八糟的问题,尽量不要采用这种模型吧。

论坛徽章:
0
3 [报告]
发表于 2010-07-05 12:34 |只看该作者
连接数到达MAX_FD了吧。还有用多线程时关闭socket用shutdown()不要直接close(),close()会直接减少引用计数。

论坛徽章:
0
4 [报告]
发表于 2010-07-05 12:39 |只看该作者
好吧,期待大牛。

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
5 [报告]
发表于 2010-07-05 13:08 |只看该作者
本帖最后由 yulihua49 于 2010-07-05 13:10 编辑
连接数到达MAX_FD了吧。还有用多线程时关闭socket用shutdown()不要直接close(),close()会直接减少引用计数 ...
没本 发表于 2010-07-05 12:34



    MAX FD=1024,256个并发连接。
日志:
  1195 5 thrsrv:19010 07/05 11:36'35 thread_work:tid=1291950400,sock=37
   1219 5 thrsrv:19010 07/05 11:36'35 thread_work:tid=1283557696,sock=37
这里57696线程乱了:
  1226 1 thrsrv:19010 07/05 11:36'35 aft NetHeadDispack len=24,PKGERR 127.0.0.1:02u0000000000000002oMG00| !9穨U皛@~`撮y]C!^C        c~N.6膈~B~P
   1227 1 thrsrv:19010 07/05 11:36'35 30 32 75 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 6F 4D 47 30 30 A0 21 39 B7 95         B0 80 7E 60 B4 E9 79 5D 43 21 03 63 8E 2E 36 EB F5 82 90   1228 1 thrsrv:19010 07/05 11:36'35 thread_work:tid=1283557696,接收结束,status=84,Invalid or incomplete multibyte or wide c        haracter
50400线程退出:
328551 1  07/05 12:06'35 RecvPack:head TIMEOUT 1800 second's
328552 1  07/05 12:06'35 thread_work:tid=1291950400,接收结束,status=84,Invalid or incomplete multibyte or wide character

以上时间内50400和57696共用37socket,互相扰乱了。

论坛徽章:
324
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
6 [报告]
发表于 2010-07-05 13:09 |只看该作者
ShowLog(1,"%s:accept err=%d,%s",__FUNCTION__,errno,strerror(errno));

                        switch(errno) {

后面switch里面的errno很可能已经因调用ShowLog而被改了

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
7 [报告]
发表于 2010-07-05 13:12 |只看该作者
本帖最后由 yulihua49 于 2010-07-05 13:15 编辑
ShowLog(1,"%s:accept err=%d,%s",__FUNCTION__,errno,strerror(errno));

                        swit ...
hellioncu 发表于 2010-07-05 13:09



   s=37, 不小于0, 没走到那里,ShowLog用的srderr ,fd=2;

之前有这么一句,有关系吗?
leng=1;
        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&leng,sizeof(leng));

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
8 [报告]
发表于 2010-07-05 13:20 |只看该作者
select尽量不要和多并发thread用吧,nginx代码里就禁止这么使用。至于原因嘛,我也不知道,只是前些天看到n ...
lenky0401 发表于 2010-07-05 12:23



    1.select在主线程用,并未涉及子线程。
   2.光使用accept也出现过这问题,先是usleep(1000),后来改10000.
实际上,usleep至少休息4000微秒。

论坛徽章:
0
9 [报告]
发表于 2010-07-05 13:45 |只看该作者
Conn里的内容在创建线程的时候会被共用.比如你要创建两个线程,线程1里的Conn内容会被主线程改变.简单点说主线程先创建线程1,当要创建线程2的时候,Conn的内容已经变了,这时如果线程1还没结束,那么引用的Conn的地方就会混乱.

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
10 [报告]
发表于 2010-07-05 14:29 |只看该作者
本帖最后由 yulihua49 于 2010-07-05 14:31 编辑
Conn里的内容在创建线程的时候会被共用.比如你要创建两个线程,线程1里的Conn内容会被主线程改变.简单点说主 ...
samlumengjun 发表于 2010-07-05 13:45



    conn的内容在子线程被拷贝出来。
但有可能被共用,我看看。

static void * thread_work(void *param)
{
T_Connect Conn=*(T_Connect *)param;
。。。。。。。
是否拷贝时就冲突了???
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP