项目需要在线程中网络通讯,当线程开启时,先是connect到一个socket,之后再循环读写数据,要求停止线程时,是将循环条件置为false,再等待线程结束,但网络情况不佳时,connect需要几十秒,如果要在这几十秒内结束线程,主线程将需要等待几十秒,导致界面卡死。
昨天我将socket改成非阻塞的,然后用select循环检测(要求结束线程时将等待时间置0),select的最后一个参数是timeval{0,1},代码在虚拟机(Win7 x86)下大部分时间能正常运行(线程多几个就可能出问题),但拖到物理机上发现select老是返回-1,连接成功的机率非常低。
而且,如果将timeval设置成{0,0},即使是在虚拟机下也是一直返回-1。
部分代码如下:
unsigned long ul = 1;
#ifdef WIN32
ioctlsocket(fd, FIONBIO, &ul); /// 设置为非阻塞模式
#elif
ioctl(fd, FIONBIO, &ul); /// 设置为非阻塞模式
#endif
if (connect(fd, (struct sockaddr *)&raddr, sizeof(struct sockaddr_in)) == -1)
{
int error = 0;
int len = 0;
bool success = false;
timeval tm = { 0, 1 }; /// 超时1ms
fd_set set;
FD_ZERO(&set);
FD_SET(fd, &set);
fd_set set_back = set;
clock_t start_time = clock();
/// 有限时间内循环检测connect是否成功
do
{
int res = select(fd + 1, NULL, &set, NULL, &tm);
log_print("%d\t%d\n", fd, res);
if (res > 0)
{
if (FD_ISSET(fd, &set))
{
#ifdef WIN32
getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
#elif
getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
#endif
if(error == 0)
{
success = true;
break; /// 连接成功
}
}
Sleep(1);
set = set_back;
error = 0;
}
} while (clock() - start_time < _connect_time_out);
if ( ! success)
{
log_print("rua_init_connect::connect %s:%u fail!!!\r\n", p_rua->ripstr, p_rua->rport);
closesocket(fd);
return false;
}
ul = 0;
#ifdef WIN32
ioctlsocket(fd, FIONBIO, &ul); /// 还原为阻塞模式
#elif
ioctl(sockfd, FIONBIO, &ul); /// 还原为阻塞模式
#endif
}
timeval tm = { 0, 1 }; /// 超时1ms
超时设置的太苛刻了,怎么着也得有个 2 秒吧。
因为线程最多可能达到64个,如果每个都超时2秒,那么最坏情况下,我要关闭这64个线程,就得等待两分钟了。
最难以理解的是在虚拟机下可以,到物理机下就不行,环境什么的都是样的。
@BYSF_XF: 在 select 模式下,你直接关闭 socket ,select 会返回而不会等到超时。
@BYSF_XF: 如果你认为 connect 肯定会在 1 毫秒内成功,那么请你去调整你的硬件和网络环境,让它达到这个水准。否则,请你在做网络编程时一定要记住,connect 的执行时间是不定的,你必须考虑超时的处理,这样你才能编写网络程序。
@Launcher: 我在外面加了个循环的,如果直接让select等待1秒,那么我在外面就不好立即停止这个线程了。
@Launcher: 这个select不是用来控制读写的,只是用来检测连接是否成功的。
@Launcher: 当一个线程中有阻塞函数调用时,如何安全又快速的结束这个线程?如果能解决这个,就不需要这么麻烦了(线程是直接用Windows api和Linux API创建的,不是java)。
@BYSF_XF: 其实你是来问如何正常退出一个线程的,加一个成员变量 fShutdown,默认为 false,当你想停止执行此循环的线程时,将其设置为 true.
do
{
///
} while(clock() - start_time < _connect_time_out || !fShutdown);
@Launcher: 但在循环之前有一个connect
@BYSF_XF: 你不是设置了非阻塞模式吗? 非阻塞连接时,此方法不会等待连接建立后才返回,你通过了后面的 select 来检测 connect 方法的结果。难道你的阻塞是发生在 connect,而不是在 select 阶段?
@Launcher: 多线程的非阻塞模式,貌似有些小bug,我现在还是用阻塞的connect了,然后用专开一个线程用于回收这些线程。
换了一种方式解决了这个问题,还是非常感谢你热心的帮助