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

查看:45
本文介绍了唤醒线程在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)\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();

}

推荐答案

您可以使用管道来通知线程您希望其退出.然后,您可以进行 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天全站免登陆