为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

拉风的咖菲猫 2019-12-03 16:14:57
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();}
查看完整描述

3 回答

?
HUH函数

TA贡献1836条经验 获得超4个赞

您可以使用管道来通知线程您希望其退出。然后,您可以select()在管道和监听套接字上进行选择的呼叫。


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


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

}

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


查看完整回答
反对 回复 2019-12-03
?
三国纷争

TA贡献1804条经验 获得超7个赞

使用shutdown()调用关闭套接字。这将唤醒所有阻塞的线程,同时保持文件描述符有效。

close()在描述符上,另一个线程B使用具有内在的危险:另一个线程C可能会打开一个新的文件描述符,然后线程B将使用该文件描述符而不是已关闭的文件描述符。dup2()/dev/null到就避免了这个问题,但没有醒来阻塞的线程可靠。

请注意,shutdown()仅适用于套接字-对于其他类型的描述符,您可能需要select + pipe-to-self或cancel方法。


查看完整回答
反对 回复 2019-12-03
?
MMMHUHU

TA贡献1834条经验 获得超8个赞

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


查看完整回答
反对 回复 2019-12-03
  • 3 回答
  • 0 关注
  • 662 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信