唤醒线程在accept()调用中被阻止

Linux上的套接字问题


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


当需要退出程序时,如何发信号通知该网络工作线程(从主线程)从accept()调用返回,同时仍然能够正常退出其循环并处理其清理代码。


我尝试过的一些事情:


pthread_kill发送信号。感觉很笨拙,而且不能可靠地使线程执行关闭逻辑。也使程序终止。我想尽可能避免发出信号。


pthread_cancel。同上。这是对线程的残酷杀戮。那,线程可能正在做其他事情。


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


一些约束:


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


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


本质上,我要寻找的逻辑看起来像这样。


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)\n", sock, errno);

        }

        else

        {

            HandleNewNetworkCondition(sock, &listenaddr);

        }

    }


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

    return NULL;

}


void SignalHandler(int sig)

{

    printf("Caught CTRL-C\n");

}


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...\n");


    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();


}


拉风的咖菲猫
浏览 663回答 3
3回答

HUH函数

您可以使用管道来通知线程您希望其退出。然后,您可以select()在管道和监听套接字上进行选择的呼叫。例如(编译但未完全测试):// NotifyPipe.h#ifndef NOTIFYPIPE_H_INCLUDED#define NOTIFYPIPE_H_INCLUDEDclass NotifyPipe{&nbsp; &nbsp; &nbsp; &nbsp; int m_receiveFd;&nbsp; &nbsp; &nbsp; &nbsp; int m_sendFd;&nbsp; &nbsp; public:&nbsp; &nbsp; &nbsp; &nbsp; NotifyPipe();&nbsp; &nbsp; &nbsp; &nbsp; virtual ~NotifyPipe();&nbsp; &nbsp; &nbsp; &nbsp; int receiverFd();&nbsp; &nbsp; &nbsp; &nbsp; void notify();};#endif // NOTIFYPIPE_H_INCLUDED// NotifyPipe.cpp#include "NotifyPipe.h"#include <unistd.h>#include <assert.h>#include <fcntl.h>NotifyPipe::NotifyPipe(){&nbsp; &nbsp; int pipefd[2];&nbsp; &nbsp; int ret = pipe(pipefd);&nbsp; &nbsp; assert(ret == 0); // For real usage put proper check here&nbsp; &nbsp; m_receiveFd = pipefd[0];&nbsp; &nbsp; m_sendFd = pipefd[1];&nbsp; &nbsp; fcntl(m_sendFd,F_SETFL,O_NONBLOCK);}NotifyPipe::~NotifyPipe(){&nbsp; &nbsp; close(m_sendFd);&nbsp; &nbsp; close(m_receiveFd);}int NotifyPipe::receiverFd(){&nbsp; &nbsp; return m_receiveFd;}void NotifyPipe::notify(){&nbsp; &nbsp; write(m_sendFd,"1",1);}然后select使用receiverFd(),并使用通知终止notify()。

三国纷争

使用shutdown()调用关闭套接字。这将唤醒所有阻塞的线程,同时保持文件描述符有效。close()在描述符上,另一个线程B使用具有内在的危险:另一个线程C可能会打开一个新的文件描述符,然后线程B将使用该文件描述符而不是已关闭的文件描述符。dup2()一/dev/null到就避免了这个问题,但没有醒来阻塞的线程可靠。请注意,shutdown()仅适用于套接字-对于其他类型的描述符,您可能需要select + pipe-to-self或cancel方法。

MMMHUHU

如果pthread实现无法正确实现取消,则pthread_cancel取消被accept()阻塞的线程是有风险的,也就是说,如果线程创建了一个套接字,则在返回代码之前,将为其调用pthread_cancel(),该线程为取消,并且新创建的套接字泄漏。尽管FreeBSD 9.0和更高版本没有这种竞争状况问题,但是您应该首先检查操作系统。
打开App,查看更多内容
随时随地看视频慕课网APP