唤醒被 accept() 调用阻塞的线程 [英] Wake up thread blocked on accept() call

查看:36
本文介绍了唤醒被 accept() 调用阻塞的线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Linux 上的套接字问题

Sockets on Linux question

我有一个在 accept() 调用中被阻塞的工作线程.它只是等待传入的网络连接,处理它,然后返回侦听下一个连接.

I have a worker thread that is blocked on an accept() call. It simply waits for an incoming network connection, handles it, and then returns to listening for the next connection.

当程序需要退出时,我如何通知这个网络工作线程(来自主线程)从 accept() 调用返回,同时仍然能够优雅地退出其循环并处理其清理代码.

When it is time for the program to exit, how do I signal this network worker thread (from the main thread) to return from the accept() call while still being able to gracefully exit its loop and handle its cleanup code.

我尝试过的一些事情:

  1. pthread_kill 发送信号.这样做感觉很笨拙,而且它不能可靠地允许线程执行它的关闭逻辑.也使程序终止.如果可能的话,我想避免信号.

  1. pthread_kill to send a signal. Feels kludgy to do this, plus it doesn't reliably allow the thread to do it's shutdown logic. Also makes the program terminate as well. I'd like to avoid signals if at all possible.

pthread_cancel.和上面一样.这是对线程的严厉杀戮.那个,线程可能正在做其他事情.

pthread_cancel. Same as above. It's a harsh kill on the thread. That, and the thread may be doing something else.

从主线程关闭监听套接字以使accept() 中止.这不能可靠地工作.

Closing the listen socket from the main thread in order to make accept() abort. This doesn't reliably work.

一些限制:

如果解决方案涉及使侦听套接字非阻塞,那很好.但我不想接受这样的解决方案,该解决方案涉及每隔几秒钟通过 select 调用唤醒线程以检查退出条件.

If the solution involves making the listen socket non-blocking, that is fine. But I don't want to accept a solution that involves the thread waking up via a select call every few seconds to check the exit condition.

退出的线程条件可能与退出的进程无关.

The thread condition to exit may not be tied to the process exiting.

基本上,我想要的逻辑看起来像这样.

Essentially, the logic I am going for looks like this.

void* WorkerThread(void* args)
{
    DoSomeImportantInitialization();  // initialize listen socket and some thread specific stuff

    while (HasExitConditionBeenSet()==false)
    {
        listensize = sizeof(listenaddr);
        int sock = accept(listensocket, &listenaddr, &listensize);

        // check if exit condition has been set using thread safe semantics
        if (HasExitConditionBeenSet())
        {
            break;
        }

        if (sock < 0)
        {
            printf("accept returned %d (errno==%d)
", sock, errno);
        }
        else
        {
            HandleNewNetworkCondition(sock, &listenaddr);
        }
    }

    DoSomeImportantCleanup(); // close listen socket, close connections, cleanup etc..
    return NULL;
}

void SignalHandler(int sig)
{
    printf("Caught CTRL-C
");
}

void NotifyWorkerThreadToExit(pthread_t thread_handle)
{
    // signal thread to exit
}

int main()
{
    void* ptr_ret= NULL;
    pthread_t workerthread_handle = 0;

    pthread_create(&workerthread, NULL, WorkerThread, NULL);

    signal(SIGINT, SignalHandler);

    sleep((unsigned int)-1); // sleep until the user hits ctrl-c

    printf("Returned from sleep call...
");

    SetThreadExitCondition(); // sets global variable with barrier that worker thread checks on

    // this is the function I'm stalled on writing
    NotifyWorkerThreadToExit(workerthread_handle);

    // wait for thread to exit cleanly
    pthread_join(workerthread_handle, &ptr_ret);

    DoProcessCleanupStuff();

}

推荐答案

您可以使用管道通知线程您希望它退出.然后你可以有一个 select() 调用,它在管道和监听套接字上进行选择.

You can use a pipe to notify the thread that you want it to exit. Then you can have a select() call which selects on both the pipe and the listening socket.

例如(编译但未完全测试):

For example (compiles but not fully tested):

// NotifyPipe.h
#ifndef NOTIFYPIPE_H_INCLUDED
#define NOTIFYPIPE_H_INCLUDED

class NotifyPipe
{
        int m_receiveFd;
        int m_sendFd;

    public:
        NotifyPipe();
        virtual ~NotifyPipe();

        int receiverFd();
        void notify();
};

#endif // NOTIFYPIPE_H_INCLUDED

// NotifyPipe.cpp

#include "NotifyPipe.h"

#include <unistd.h>
#include <assert.h>
#include <fcntl.h>

NotifyPipe::NotifyPipe()
{
    int pipefd[2];
    int ret = pipe(pipefd);
    assert(ret == 0); // For real usage put proper check here
    m_receiveFd = pipefd[0];
    m_sendFd = pipefd[1];
    fcntl(m_sendFd,F_SETFL,O_NONBLOCK);
}


NotifyPipe::~NotifyPipe()
{
    close(m_sendFd);
    close(m_receiveFd);
}


int NotifyPipe::receiverFd()
{
    return m_receiveFd;
}


void NotifyPipe::notify()
{
    write(m_sendFd,"1",1);
}

然后用receiverFd()select,并用notify()通知终止.

Then select with receiverFd(), and notify for termination using notify().

这篇关于唤醒被 accept() 调用阻塞的线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆