背景

多线程模式下,偶尔会遇到recvfrom在非阻塞模式下不能正常返回的情况,使得系统出现假死锁,从而导致整个系统不能提供服务。

剖析

在上图中,如果threadB在进行recvfrom调用时,sockfd已经被关闭了,而threadD又重新创建了sockfd且值刚好与原来的值相同(即新创建的sockfd依然是6),此时sockfd还未设置为非阻塞模式,这样在多线程模式下就出现sockfd的非线程安全访问,导致recvfrom调用会出现概率性卡死问题。

案例

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <stdarg.h>
#include <poll.h>static int gSocket = -1;#define NONBLOCK 1
#define SER_PORT 6666#define TIMESTAMPE ({                                           \char timeStampe[128] = {0};                                 \do {                                                        \struct timeval now = {0};                               \struct tm *tm = NULL;                                   \gettimeofday(&now, NULL);                               \tm = localtime(&now.tv_sec);                            \if (!tm)                                                \{                                                       \break;                                              \}                                                       \snprintf(timeStampe, sizeof(timeStampe) - 1,            \"[%04d-%02d-%02d %02d:%02d:%02d:%06ld]",            \tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,    \tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec);  \} while (0);                                                \timeStampe;                                                 \
})static void doPrint(const char *file, int line, const char *format, ...)
{char buffer[1024] = {0};va_list vlist;va_start(vlist, format);vsnprintf(buffer, sizeof(buffer) - 1, format, vlist);va_end(vlist);fprintf(stdout, "%s %s:%04d[%05ld] -- %s\n", TIMESTAMPE, file, line, syscall(SYS_gettid), buffer);
}#define dolog(...) do {                         \doPrint(__FILE__, __LINE__, __VA_ARGS__);   \
} while (0)#define setNonBlock(sockfd) do {                    \int flags = fcntl((sockfd), F_GETFL);           \fcntl((sockfd), F_SETFL, flags | O_NONBLOCK);   \
} while (0)#define isNonBlock(sockfd) ({                       \int flags = 0;                                  \flags = fcntl((sockfd), F_GETFL) & O_NONBLOCK;  \flags;                                          \
})static void *runThread(void *arg)
{int flags = -1;while (1) {
#if NONBLOCKgSocket = socket(AF_INET, SOCK_DGRAM, 0);setNonBlock(gSocket);
#elsegSocket = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
#endifstruct sockaddr_in address = {.sin_family = AF_INET,.sin_port   = htons(SER_PORT),.sin_addr   = {.s_addr = inet_addr("127.0.0.1")}};const int reuseFlag = 1;setsockopt(gSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseFlag, sizeof reuseFlag);bind(gSocket, (struct sockaddr *)&address, sizeof(address));//dolog("Create gSocket[%d], %s", gSocket, strerror(errno));usleep(400);close(gSocket);gSocket = -1;}
}static void *clientThread(void *arg)
{char buffer[] = {"I am a client thread."};while (1) {
#if NONBLOCKint sockfd = socket(AF_INET, SOCK_DGRAM, 0);setNonBlock(gSocket);
#elseint sockfd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
#endifstruct sockaddr_in serverAddr = {.sin_family = AF_INET,.sin_port   = htons(SER_PORT),.sin_addr   = {.s_addr = inet_addr("127.0.0.1")}};socklen_t addrlen = sizeof(serverAddr);int retval = sendto(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&serverAddr, addrlen);//dolog("sendto '%d' byte, %s", retval, strerror(errno));usleep(200);close(sockfd);}
}int main(int argc, const char *argv[])
{struct sockaddr_in address = {0};pthread_t pthread1 = -1;pthread_create(&pthread1, NULL, runThread, NULL);pthread_t pthread2 = -1;pthread_create(&pthread2, NULL, clientThread, NULL);while (1) {struct pollfd pfd = {.fd     = gSocket,.events = POLLIN,};int retval = poll(&pfd, 1, 1000);if (retval > 0) {char buffer[1024] = {0};int len = sizeof(address);int retval = recvfrom(gSocket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)(&address), &len);dolog("gSocket[%d], retval[%d], reason[%s], %s", gSocket, retval, strerror(errno), buffer);} else {dolog("gSocket[%d] poll error, reason[%s]", gSocket, (0 == retval) ? "timeout" : strerror(errno));}usleep(200);}sleep(10);return 0;
}

#define NONBLOCK 1

当NONBLOCK为1时,上述案例在运行不久后会出现recvfrom卡死的情况。

#define NONBLOCK 0

当NONBLOCK为0时,上述案例不会出现卡死情况。

总结

在多线程模式下,要尽量保证sockfd的原子操作;如果不能保证,就需要保证sockfd在创建成功时就是非阻塞模式,这样可以避免出现一些隐藏的问题,如下操作。

gSocket = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);

非阻塞recvfrom卡住相关推荐

  1. 非阻塞recvfrom的设置

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 我想用U ...

  2. recvfrom函数 非阻塞_那些年让你迷惑的阻塞、非阻塞、异步、同步

    那些年让你迷惑的阻塞.非阻塞.异步.同步 在IT圈混饭吃,不管你用什么编程语言.从事前端还是后端,阻塞.非阻塞.异步.同步这些概念,都需要清晰地掌握,否则,怎么与面试官谈笑风生(chui niu pi ...

  3. linux socket write()函数阻塞卡住线程问题(线程无法结束)write()非阻塞代码

    文章目录 1.参考文章:C++网络通信中write和read的为什么会阻塞 [2.参考文章:网络编程(24)-- linux中write和read函数的阻塞试验](https://blog.csdn. ...

  4. python gevent模块 下载_Python协程阻塞IO非阻塞IO同步IO异步IO

    Python-协程-阻塞IO-非阻塞IO-同步IO-异步IO 一.协程 协程又称为微线程 CPU 是无法识别协程的,只能识别是线程,协程是由开发人员自己控制的.协程可以在单线程下实现并发的效果(实际计 ...

  5. JAVA那点破事!并发、IO模型、集合、线程池、死锁、非阻塞、AQS....

    关于Java面试,面试官一般喜欢问哪些问题? 本文对一些高频问题做了汇总,为了便于大家查找问题,了解全貌,整理个目录,我们可以快速全局了解关于 JAVA 接下来,我们逐条来看看每个问题及答案 JDK. ...

  6. Socket阻塞,非阻塞,同步,异步

    1.socket 阻塞,非阻塞(select) http://blog.csdn.net/piaojun_pj/article/details/5991968/ http://blog.chinaun ...

  7. python复制文件夹不阻塞_Python学习第54天(阻塞(blocking) IO和非阻塞(non-blocking)IO)...

    今天之所以这么早结束,主要是因为自己脑子不够用了,发现最近的定义有点多,完全搞不清楚了,打算早点睡觉,今天的内容估计要引用很多别人的部分了. 看到题目的四个东东是不是惊呆了,我也是惊呆了,同时脑子还跟 ...

  8. socket的阻塞非阻塞方法在缓冲区的差别

    转载:http://blog.csdn.net/jwybobo2007/article/details/6164362 一.发送选用send(这里特指TCP)以及sendto(这里特指UDP)来描述 ...

  9. 迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)...

    常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据. 如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTT ...

最新文章

  1. VMware workstation中rhel安装VMware tools失败
  2. gradle仓库配置
  3. 湖南网络推广浅谈网站首页降权怎么办?
  4. Windows核心编程 第八章 用户方式中线程的同步(下)
  5. 发现1个宝藏项目,GitHub上都没有的那种!
  6. 了解自己计算机硬件设备信息
  7. 计算与推断思维 翻译完成
  8. 用JS实现版面拖拽效果
  9. 如何在Linux中查看所有正在运行的进程 1
  10. 【PowerShell】PS中 the fuck 插件(PoShFuck)将 wtf 搜索引擎从 Google 改为 Baidu 或者 Bing
  11. Qt5_自定义处理Windows消息函数
  12. 模拟幅度调制系统抗干扰性能仿真分析[模板]
  13. latex在行末出现百分号的作用
  14. BUUCTF--[第二章 web进阶]死亡ping命令
  15. 关于商业计划书(Business Project,以下简称BP)写作那些事儿(一)
  16. 人大金仓数据库的备份与还原
  17. java中怎么自己画地图_用 4 行代码画一幅中国地图
  18. H5调用微信扫一扫识别二维码
  19. 学习笔记12-SG90舵机
  20. android 投屏代码,android投屏技术:控制设备源码分析

热门文章

  1. Mac Google浏览器出现:您目前无法访问 XX.XX.XX.XX,因为此网站发送了 Google Chrome 无法处理的杂乱凭据
  2. 怎么的测试用例是一个好的测试用例?
  3. 论文阅读:Gradient Harmonized Single-stage Detector
  4. JS-108~161
  5. 自注意力机制与注意力机制
  6. php new object delete,DeleteObject()函数
  7. 阿里云服务器挖矿程序解决流程
  8. OpenWrt 固件编译教程
  9. 【互联网及其应用】第3章网络技术基础
  10. Mysql复习资料整理