当从另一个线程发出信号时,如何从阻塞套接字上的“accept”中“脱离”?

时间:2013-05-07 18:37:03

标签: linux multithreading sockets

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

问题:

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

解决方案:

  

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

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

  1. 我应该做的答案是什么意思?
  2. 如果线程1信号处理程序可以做一些会导致accept立即返回的事情,为什么线程2在没有信号的情况下也不能这样做?
  3. 在没有信号的情况下还有另一种方法吗?我不想增加图书馆的警告。

5 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:0)

只需关闭侦听套接字,并从accept()处理生成的错误或异常。

答案 3 :(得分:0)

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

这似乎有效,但文档并没有真正解释它在线程之间的操作 - 它似乎只是起作用 - 所以如果有人能澄清这一点,我会接受这个作为答案。

答案 4 :(得分:0)

我相信信号可以在不增加图书馆警告的情况下使用#34;。请考虑以下事项:

#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()更容易推断资源泄漏。
  • 它不需要任何额外资源(例如管道)。
  • 可以增强它以支持多个并发任务。
  • 这是一个很好的样板。
  • 它甚至可以编译。 : - )