唤醒线程在accept()调用时被阻塞

时间:2010-03-21 07:38:03

标签: c sockets pthreads

Linux上的套接字问题

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

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

我尝试过的一些事情:

  1. pthread_kill发送信号。感觉很难做到这一点,加上它不能可靠地允许线程执行它的关闭逻辑。也使程序终止。如果可能的话,我想避免发出信号。

  2. pthread_cancel可以。与上述相同。这是对线程的严厉杀戮。那个,线程可能正在做其他事情。

  3. 从主线程关闭listen套接字以使accept()中止。这无法可靠地发挥作用。

  4. 一些限制:

    如果解决方案涉及使监听套接字无阻塞,那很好。但我不想接受一个解决方案,该解决方案涉及线程每隔几秒钟通过一次选择呼叫唤醒以检查退出条件。

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

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

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

4 个答案:

答案 0 :(得分:50)

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

另一个线程B正在使用的描述符上的

close()本质上是危险的:另一个线程C可以打开一个新的文件描述符,然后线程B将使用而不是关闭的线程。 dup2()一个/dev/null可以避免这个问题,但不会可靠地唤醒被阻塞的线程。

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

答案 1 :(得分:16)

您可以使用管道通知线程您希望它退出。然后,您可以进行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()通知终止。

答案 2 :(得分:0)

关闭侦听套接字并接受将返回错误。

什么不能可靠地解决这个问题?描述你所面临的问题。

答案 3 :(得分:0)

如果pthread实现没有正确实现取消,那么取消在accept()中被阻塞的线程的pthread_cancel是有风险的,也就是说,如果线程创建了一个套接字,就在返回代码之前,为它调用了pthread_cancel(),线程被取消,新创建的套接字被泄露。虽然FreeBSD 9.0及更高版本没有这样的竞争条件问题,但你应该先检查你的操作系统。