可以“随时”终止的socket线程

很多人在刚学socket时,都是在线程中connect,然后while(flag) read();要停止这个线程时将falg置false,再wait,甚至直接termination(这种方式终止线程的安全隐患不在这里论述)。

一般情况下的确没什么问题,但拿到一个真正的项目中时,就不太好了,这样写就很可能出现这样的情况:网络情况不好时,connect和read可能需要很长时间才能返回,将flag置false时,可能还需要等待很久(最大等待时间和平台有关),也许长达几分钟,特别是在GUI线程中去停止这个线程时,很可能会造成界面卡死。

那么该如何解决这种问题呢?我的做法是,在connect之前,将socket置为非阻塞模式,这时再使用connect时,会立即返回,我们判断它的返回值,如果没有连接成功(内核还在继续为我们建立连接),即使用select去监测它,发现它连接成功时,再不愿成阻塞模式。

这样做还不够,因为select也是阻塞函数,但select是可以监测多个文件描述符的,我们可以创建一个pipe(还有eventfd之类),让select同时监测pipe和socket,在我们需要停止connect时,向pipe的写端写入数据,此时select就会被唤醒,当我们发现是由pipe唤醒select时,即直接释放相关资源后退出线程。

对于read部分,我们一样使用select同时监测pipe和socket,使用同样的方法退出。

这种方法相对可以更快更安全的终止线程,示例代码如下(注意,以下代码纯记事本写的,可能存在错误,仅供参考):

int MyThread::connect()
{
    int fd;
    /// 此处略过部分代码 ///

    unsigned long ul = 1;
    ioctl(fd, FIONBIO, &ul);		/// 设置为非阻塞模式
    if (connect(fd, (struct sockaddr *)&raddr, sizeof(struct sockaddr_in)) == -1)
    {
    	int error = 0;
    	int max_fd = ( fd > _pipe_fd[0] ? fd : _pipe_fd[0] ) + 1;

    	fd_set set;
    	FD_ZERO(&set);
    	FD_SET(fd, &set);

    	fd_set rset;
    	FD_ZERO(&rset);
    	FD_SET(_pipe_fd[0], &rset);	/// 将pipe读端加入select,便于线程外随时退出select

    	if ( ! (error = (select(max_fd, &rset, &set, NULL, NULL) <= 0) ) )		/// error == 0  !error == 1表示没发生错误,进一步检查错误
    	{
    		if (FD_ISSET(fd, &set))
    		{
    			int len = 0;
    			getsockopt(fd + 1, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
    		}
    		else if (FD_ISSET(_pipe_fd[0], &rset))
    		{
    		    /// 接收到退出命令
    		    /// 如果退出后还需要复用到pipe,此处建议将pipe里的东西read出来以清空
    			error = -2;
    		}
    		else
    		{
    			/// 如果发生错误,或者由pipe唤醒,则终止连接
    			error = -1;
    		}
    	}
    	if (error)
    	{
    		close(fd);
    		return -1;
    	}
    }
    ul = 0;
    ioctl(sockfd, FIONBIO, &ul);	/// 还原为阻塞模式
    return fd;
}

void MyThread::run()
{
    int fd = connect();
    if (fd < 0)
    {
        return;
    }

    fd_set rset;
    int ret;
    int max_fd = ( fd > _pipe_fd[0] ? fd : _pipe_fd[0] ) + 1;
    while (true)
    {
        FD_ZERO(&rset);
        FD_SET(fd, &rset);
        FD_SET(_pipe_fd[0], &rset);

        ret = select(fd + 1, &rset, NULL, NULL, NULL);
        if (ret < 0)
        {
            break;
        }
        if (ret)
        {
            if (FD_ISSET(_pipe_fd[0], &rset))
            {
                break;
            }
            if (FD_ISSET(fd, &rset))
            {
                read();
            }
        }
    }
    close(fd);
}

  

以种方法只是我目前的处理方式,应该还有更好的方式(有很多优秀的库目前我还没有去深入学习)。

高手们可以飘过了,不过最好留下点指导吧。

时间: 2024-10-09 06:28:26

可以“随时”终止的socket线程的相关文章

终止阻塞的线程

线程状态 我们知道,一个线程可以处于以下四种状态之一: 1. 新建(New):当线程被创建时,它只会短暂地处于这种状态.此时它已经分配了必须的系统资源,并执行了初始化.此刻线程已经有资格获取CPU时间了,之后调度器将把这个线程转变为可运行状态或阻塞状态. 2. 就绪(Runnable):在这种状态下,只要调度器将CPU时间片分给线程,线程就可以运行.也就是说,在任意时刻,线程可以运行也可以不运行. 3. 阻塞(Blocked):线程能够运行,但有某个或多个条件阻止它运行.当线程处于阻塞状态时,调

java socket线程通信

关于socket线程通信的一些知识整理 一般我们需要要让两台机子进行通信,需要创建一个Server 类,一个Client类,还需要创建一个线程类 server public class Server { public static void main(String[] args) throws IOException {  ServerSocket ss = new ServerSocket(8888);  int num=0;  System.out.println("服务器即将启动,   等

Java中终止正在运行线程

问题:java 中如何让一个正在运行的线程终止掉? Demo_1: class TT implements Runnable { private boolean flag = true; @Override public void run() { int i = 0; while(flag) { System.out.println("child thread: "+i++); } } public void shutDowm() { flag = false; } } class T

线程的终止stop与线程的中断interrupted

线程除了运行完毕,自动进入死亡状态,也可以手动进行停止,Thread类也提供了2个类方法来进行线程的停止. 一.stop 如图所示,stop方法已经被标记为过时的,不推荐的.因为这这个方法太过于暴力,会立即杀死进程,导致数据不能同步,带来很难排查的错误. 下面是一段造成错误信息的代码: 1 public class StopThreadUnsafe { 2 public static User u = new User(); 3 4 public static class User { 5 pr

立即终止Sleep的线程

在实际工作中,我们需要每隔几分钟从API取数. while(isRunning) { work(); Thread.Sleep(5*60*1000); } 如果设置isRunning=false,也需要等待几分钟才能终止. 如果调用Thread.Abort()会报异常. 解决方法,将一次Sleep 5分钟,改为分5*60次Sleep一秒钟.然后将isRunning=false即可立即终止线程. while(isRunning) { work(); for(int i=0;i<5*60 &&a

java实现终止javaw.exe线程

public static void main(String[] args) throws IOException { Process process = Runtime.getRuntime().exec("cmd.exe /c tasklist |findstr \"javaw\""); Scanner in = new Scanner(process.getInputStream()); while (in.hasNextLine()) { String p

Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public class Thread implements Runnable { /* What will be run. */ private Runnable target; ...... /** * Causes this thread to begin execution; the Java Virtu

【C/C++多线程编程之四】终止pthread线程

多线程编程之终止pthread线程  Pthread是 POSIX threads 的简称,是POSIX的线程标准.           终止线程似乎是多线程编程的最后一步,但绝不是本系列教材的结束.线程创建到线程终止,希望先给读者一个关于多线程编程的总体认识.           1.终止Pthread线程:pthread_exit()           參数:           ret:地址指针,本质上是返回值写入的地址.           终止线程是线程的主动行为,一个线程调用pth

Java并发编程-如何终止线程

我们知道使用stop().suspend()等方法在终止与恢复线程有弊端,会造成线程不安全,那么问题来了,应该如何正确终止与恢复线程呢?这里可以使用两种方法: 1.使用interrupt()中断方法. 2.使用volatile boolean变量进行控制. 在使用interrupt方法之前,有必要介绍一下中断以及与interrupt相关的方法.中断可以理解为线程的一个标志位属性,表示一个运行中的线程是否被其他线程进行了中断操作.这里提到了其他线程,所以可以认为中断是线程之间进行通信的一种方式,简