【问题标题】:how to wakeup select() within timeout from another thread如何在超时内从另一个线程唤醒 select()
【发布时间】:2013-06-29 20:32:36
【问题描述】:

根据“man select”信息:

     "On  success,  select() and pselect() return the number of file descrip‐
   tors contained in the three returned  descriptor  sets which may be zero 
   if the timeout expires  before  anything  interesting happens.  On error, 
   -1 is returned, and errno is set appropriately; the sets and timeout become
   undefined, so do not  rely  on  their  contents after an error."

Select 将被唤醒,因为:

       1)read/write availability       
       2)select error                 
       3)descriptoris closed.  

但是,如果没有可用数据并且 select 仍在超时内,我们如何从另一个线程中唤醒 select()?

[更新]
伪代码

          // Thread blocks on Select
          void *SocketReadThread(void *param){
               ...
               while(!(ReadThread*)param->ExitThread()) {
                   struct timeval timeout;
                   timeout.tv_sec = 60; //one minute
                   timeout.tv_usec = 0;

                   fd_set rds;
                   FD_ZERO(&rds);
                   FD_SET(sockfd, &rds)'

                   //actually, the first parameter of select() is 
                    //ignored on windows, though on linux this parameter
                   //should be (maximum socket value + 1)
                   int ret = select(sockfd + 1, &rds, NULL, NULL, &timeout );
                   //handle the result
                   //might break from here

               }
               return NULL;
          }

          //main Thread
          int main(){
                //create the SocketReadThread
                ReaderThread* rthread = new ReaderThread;
                pthread_create(&pthreadid, NULL, SocketReaderThread, 
                          NULL, (void*)rthread);

                 // do lots of things here
                 ............................

                //now main thread wants to exit SocketReaderThread
                //it sets the internal state of ReadThread as true
                rthread->SetExitFlag(true);
                //but how to wake up select ??????????????????
                //if SocketReaderThread currently blocks on select
          }

[更新]
1)@trojanfoe提供了一个方法来实现这一点,他的方法将socket数据(可能是脏数据或者退出消息数据)写入wakeup select。我将在那里进行测试并更新结果。
2) 还有一点要提一下,关闭套接字并不能保证唤醒选择函数调用,请参阅this post

[更新2]
在做了许多测试之后,这里有一些关于唤醒 select 的事实:
1) 如果 select 监视的套接字被 另一个应用程序 关闭,则 select() 调用 会立即醒来。此后,从套接字读取或写入套接字将返回 0 且 errno = 0
2) 如果select监听的socket被同一个应用的另一个线程关闭了, 如果没有数据要读取或写入,则 select() 直到超时才会唤醒。选择超时后,进行读/写操作会导致错误,errno = EBADF (因为在超时期间socket已经被另一个线程关闭了)

【问题讨论】:

  • 写入关闭的连接不会返回零。只有阅读才能做到这一点。

标签: c++ linux multithreading sockets tcp


【解决方案1】:

我使用基于pipe()的事件对象:

IoEvent.h:

#pragma once

class IoEvent {
protected:
    int m_pipe[2];
    bool m_ownsFDs;

public:
    IoEvent();              // Creates a user event
    IoEvent(int fd);        // Create a file event

    IoEvent(const IoEvent &other);

    virtual ~IoEvent();

    /**
     * Set the event to signalled state.
     */
    void set();

    /**
     * Reset the event from signalled state.
     */
    void reset();

    inline int fd() const {
        return m_pipe[0];
    }
};

IoEvent.cpp:

#include "IoEvent.h"
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>

using namespace std;

IoEvent::IoEvent() : 
    m_ownsFDs(true) {
    if (pipe(m_pipe) < 0)
        throw MyException("Failed to create pipe: %s (%d)", strerror(errno), errno);

    if (fcntl(m_pipe[0], F_SETFL, O_NONBLOCK) < 0)
        throw MyException("Failed to set pipe non-blocking mode: %s (%d)", strerror(errno), errno);
}

IoEvent::IoEvent(int fd) : 
    m_ownsFDs(false) {
    m_pipe[0] = fd;
    m_pipe[1] = -1;
}

IoEvent::IoEvent(const IoEvent &other) {
    m_pipe[0] = other.m_pipe[0];
    m_pipe[1] = other.m_pipe[1];
    m_ownsFDs = false;
}

IoEvent::~IoEvent() {
    if (m_pipe[0] >= 0) {
        if (m_ownsFDs)
            close(m_pipe[0]);

        m_pipe[0] = -1;
    }

    if (m_pipe[1] >= 0) {
        if (m_ownsFDs)
            close(m_pipe[1]);

        m_pipe[1] = -1;
    }
}

void IoEvent::set() {
    if (m_ownsFDs)
        write(m_pipe[1], "x", 1);
}

void IoEvent::reset() {
    if (m_ownsFDs) {
        uint8_t buf;

        while (read(m_pipe[0], &buf, 1) == 1)
            ;
    }
}

你可以放弃m_ownsFDs 成员;我什至不确定我是否会再使用它。

【讨论】:

  • 您可能希望更明确地说明它起作用的原因是因为您在另一个线程中有 select() 调用正在监视 m_pipe[0] 以便它在准备就绪时唤醒-阅读...
  • @JeremyFriesner 同意;我一直在等待 OP 的一些反馈,但他似乎不感兴趣。
  • @trojanfoe 我正在阅读你的代码,思考它是如何工作的,却发现我不明白为什么这可以唤醒选择,直到我看到 Jeremy Friesner 的 cmets :_) 所以你的方法将编写套接字数据唤醒选择,对吗?
  • 是的。当与WaitForMultipleObjects() 一起使用时,它是对 Windows 事件对象的廉价模仿。我通常将它用于退出事件,所以如果我在 poll() 中,我也会收听这个 quit 事件,如果我希望循环退出,我会“设置”事件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-03
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多