6

和这个人的情况一样,但我不太明白答案。

问题:

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

解决方案:

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

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

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

5 回答 5

5

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

于 2013-05-07T21:09:38.893 回答
2

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

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

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

于 2013-05-07T18:49:16.430 回答
0

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

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

于 2013-05-08T01:08:17.573 回答
0

我相信可以在不增加“图书馆警告”的情况下使用信号。考虑以下:

#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()
  • 它不需要任何额外的资源(例如管道)。
  • 它可以被增强以支持多个并发任务。
  • 这是相当样板。
  • 它甚至可以编译。:-)
于 2015-02-21T19:18:41.627 回答
0

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

于 2013-05-07T21:19:33.207 回答