【问题标题】:How do I "disengage" from `accept` on a blocking socket when signalled from another thread?当从另一个线程发出信号时,如何从阻塞套接字上的“接受”“脱离”?
【发布时间】:2013-05-07 18:37:02
【问题描述】:

我在same situation as this guy,但我不太明白答案。

问题:

  • 线程 1 在阻塞的套接字上调用 accept。
  • 线程 2 在此套接字上调用 close。
  • 线程 1 继续阻塞。我希望它从接受中返回。

解决办法:

你应该做的是向被阻塞的线程发送一个信号 接受。这将给它 EINTR 并且它可以干净地脱离 - 并且 然后关闭套接字。不要从一个线程以外的线程关闭它 使用它。

我不知道这里该怎么做——当线程 1 接收到信号时,accept 已经阻塞,并且在信号处理程序完成后将继续阻塞。

  1. 答案的真正含义是我应该做什么?
  2. 如果线程 1 的信号处理程序可以做一些会导致 accept 立即返回的事情,为什么线程 2 不能在没有信号的情况下做同样的事情?
  3. 有没有其他方法可以在没有信号的情况下做到这一点?我不想增加对图书馆的警告。

【问题讨论】:

    标签: linux multithreading sockets


    【解决方案1】:

    不要在accept() 中阻塞,而是在select()、poll() 中阻塞,或者允许您等待多个文件描述符上的活动并使用“自管道技巧”的类似调用之一。传递给select() 的所有文件描述符都应该处于非阻塞模式。文件描述符之一应该是与accept() 一起使用的服务器套接字;如果那个变得可读,那么你应该继续打电话给accept(),它不会阻塞。除此之外,创建一个pipe(),将其设置为非阻塞,并检查读取端是否可读。不要在另一个线程的服务器套接字上调用close(),而是将一个字节的数据发送到管道写入端的第一个线程。实际的字节值无关紧要;目的只是唤醒第一个线程。当select()表示管道可读时,read()并忽略来自管道的数据,close()服务器socket,停止等待新连接。

    【讨论】:

    • 我发现了shutdown(见我自己的答案)。感谢这个想法,但它似乎不必要的复杂和hacky。
    • 好吧,我不这么认为,因为关机在未连接的套接字上不起作用。
    【解决方案2】:

    如果在接受连接之前捕获到信号,accept() 调用将返回错误代码 EINTR。所以检查返回值和错误代码,然后相应地关闭套接字。

    如果您希望完全避免使用信号机制,请在调用 accept() 之前使用 select() 来确定是否有任何传入连接准备好被接受。可以使用超时进行 select() 调用,以便您可以恢复并响应中止条件。

    我通常在检查退出/中止条件的 while 循环中调用 select(),超时时间为 1000 到 3000 毫秒。如果 select() 返回一个准备好的描述符,我调用 accept() 否则我要么循环并在 select() 上再次阻塞,要么在请求时退出。

    【讨论】:

    • 那么如果有任何信号发送到线程,它会返回吗?这种事情有无操作信号吗?
    • 我相信是的。实际的信号类型会影响处理程序的操作,但许多阻塞系统调用将中止,让您有机会做您想做的事情。设置您自己的信号处理程序以捕获特定信号并从控制线程发送该信号。您的处理程序不需要做任何特别的事情。 SIGINFO 可能不是一个糟糕的选择。这是相当良性的。
    • 似乎与here 的建议相矛盾...有什么想法吗?
    • 没错,信号机制不是很好,可以避免,有时在线程存在的情况下不起作用。但是,要小心,您可以使它适用于您正在谈论的事情,即释放被阻止的呼叫。当您想要针对特定​​线程而不是进程中的其他线程等时,问题就出现了。还有另一种处理您的情况的方法......我即将编辑我的答案以提供该替代方案。
    【解决方案3】:

    只需关闭监听套接字,并处理来自 accept() 的错误或异常。

    【讨论】:

    • @mark4o 在我的 Linux 系统上,如果套接字在另一个线程上关闭,select () 不会返回。
    • @SteveEmmerson:这就是为什么我的回答建议使用管道告诉select() 中的线程进行关闭,而不是在另一个线程中关闭它。
    【解决方案4】:

    从线程 2 调用 shutdown()。accept 将返回“无效参数”。

    这似乎可行,但文档并没有真正解释它跨线程的操作——它似乎只是可行——所以如果有人能澄清这一点,我会接受它作为答案。

    【讨论】:

    • 你的意思是在监听套接字上调用shutdown()?
    • POSIX 要求 shutdown() 在未连接的套接字上失败,Linux 文档同意这一点。如果您看到不同的行为,那么依赖它是不明智的,因为它可能会因系统和内核版本而异。
    【解决方案5】:

    我相信可以在不增加“库上的警告”的情况下使用信号。考虑以下几点:

    #include <pthread.h>
    #include <signal.h>
    #include <stddef.h>
    
    static pthread_t             thread;
    static volatile sig_atomic_t sigCount;
    
    /**
     * Executes a concurrent task. Called by `pthread_create()`..
     */
    static void* startTask(void* arg)
    {
        for (;;) {
            // calls to `select()`, `accept()`, `read()`, etc. 
        }
        return NULL;
    }
    
    /**
     * Starts concurrent task. Doesn't return until the task completes.
     */
    void start()
    {
        (void)pthread_create(&thread, NULL, startTask, NULL);
        (void)pthread_join(thread);
    }
    
    static void noop(const int sig)
    {
        sigCount++;
    }
    
    /**
     * Stops concurrent task. Causes `start()` to return.
     */
    void stop()
    {
        struct sigaction oldAction;
        struct sigaction newAction;
    
        (void)sigemptyset(&newAction.sa_mask);
        newAction.sa_flags = 0;
        newAction.sa_handler = noop;
        (void)sigaction(SIGTERM, &newAction, &oldAction);
    
        (void)pthread_kill(thread, SIGTERM); // system calls return with EINTR
    
        (void)sigaction(SIGTERM, &oldAction, NULL); // restores previous handling
    
        if (sigCount > 1) // externally-generated SIGTERM was received
            oldAction.sa_handler(SIGTERM); // call previous handler
    
        sigCount = 0;
    }
    

    这有以下优点:

    • 除了正常的 EINTR 处理之外,它不需要任务代码中的任何特殊内容;因此,它比使用pthread_cancel()、pthread_cleanup_push()、pthread_cleanup_pop() 和pthread_setcancelstate() 更容易推理资源泄漏。
    • 它不需要任何额外的资源(例如管道)。
    • 可以增强它以支持多个并发任务。
    • 这是相当样板。
    • 它甚至可以编译。 :-)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多