我如何通知运行libevent的线程它应该采取一些行动?

How can I inform a thread running libevent that it should take some action?

本文关键字:线程 何通知 通知 libevent 运行      更新时间:2023-10-16

我在后台线程中使用libevent来运行hiredis并订阅远程redis数据库。使用另一个SO问题的简单示例,订阅效果非常好:

招聘人员等待消息

然而,为了避免竞争条件,在主线程中添加订阅是很重要的。为了实现这一点,我创建了一个std::vector<std::string>对象,其中包含后端应该订阅的任何密钥字符串。从这个vector对象中读取数据是通过互斥体来完成的。

但是,我如何通知后端我已经添加了一些订阅?目前我使用的计时器设置为一个非常低的分辨率:

void Client::fireAndRequeueTimer(int fd, short e, void* arg)                        
{                                                                                   
    Client* client = reinterpret_cast<Client*>(arg); // the client handles the subscription to redis (via hiredis/libevent)                                                                                    
    if (client->mDisconnect)                            
        return; // the main thread wants us to exit, so we don't recreate the timer
    event* ev = &client->mTimerEvent; // some timer event object we created
    timeval tv;                                                                     
    tv.tv_sec = 0;                                                                  
    tv.tv_usec = 1000; // 1ms                                                       
    evtimer_add(ev, &tv);
    // mPendingSubscriptions is an std::vector of strings, which contain the keys that we should add subscriptions to.
    if (client->mPendingSubscriptions.size())                                       
    {                                                                               
        std::unique_lock<std::mutex> lock(client->mSubscriptionsMutex);             
        do                                                                          
        {                                                                           
            redisAsyncCommand(                                                      
                client->mContext,                                                
                Client::subCallback,                                                
                (char*)"sub",                                                       
                "SUBSCRIBE %s",                                                     
                client->mPendingSubscriptions.back().c_str());                      
            client->mPendingSubscriptions.pop_back();                               
        }                                                                           
        while (client->mPendingSubscriptions.size());                               
    }                                                                               
}                                                                                   

(注意,我使用的是libevent 1.4.x,所以EV_PERSIST等功能不存在,我必须在每个事件中重新创建计时器)。

虽然上面的工作,我不满意,原因如下:

  1. 它给后端带来了不必要的压力来持续轮询向量。
  2. 如果没有大量的注释,读者很难理解
  3. 慢;这个计时器将使订阅事件所需的时间增加1ms。这可能很重要,也可能不重要,但无论如何,这都是浪费时间。

是否有解决这个问题的方法,可以在libevent 1.4.x的范围内解决这些问题?

我个人更喜欢让目标线程在它的事件队列中添加一个eventfd(或类似的构造)。

eventfd可以被任何其他线程安全地通知,并导致目标线程调用相关的事件处理程序。

这样,您就不需要担心正确锁定libevent结构的绝对最小值,因为操作系统会为您处理这个问题。

注意:eventfd在OSX上不可用,但只要您不需要极高的事件率,可以很容易地用管道模拟。