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

Posted

技术标签:

【中文标题】如何通知运行 libevent 的线程它应该采取一些措施?【英文标题】:How can I inform a thread running libevent that it should take some action? 【发布时间】:2015-09-06 07:57:47 【问题描述】:

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

Hiredis waiting for message

但是,为了避免竞争条件,从主线程添加订阅并非易事。为了实现这一点,我创建了一个 std::vector<std::string> 对象,其中包含后端应该订阅的任何键字符串。对该向量的读取/读取是通过互斥体执行的。

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

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 等功能,我必须在每个事件中重新创建计时器)。

虽然上述方法有效,但我对此并不满意,原因如下:

    它给后端带来了不必要的压力以不断地轮询向量。 如果没有大量的 cmets,读者很难理解 很慢;这个计时器将增加多达 1 毫秒的时间来订阅一个事件。这可能很重要,也可能不重要,但无论如何都是浪费时间。

libevent 1.4.x 的范围内,是否有任何解决方案可以解决这些问题?

【问题讨论】:

不确定 libevent 是否使用 pthreads?如果是这样,您可以使用信号吗,例如SIGHUP,并设置 pthread sigmask,以便只有您的后端线程会接受信号? 【参考方案1】:

就我个人而言,我更喜欢让目标线程在其事件队列中添加一个eventfd(或类似结构)。

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

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

注意:eventfd 在 OSX 上不可用,但只要您不需要极高的事件率,就可以使用管道轻松模拟。

【讨论】:

这听起来像我需要的。我猜我需要使用 event_set(&amp;event, fd, EV_READ, fn, data) 之类的东西,但是我该如何 a) 获取 fd 和 b) "touch" fd 来触发事件? 我应该澄清一下;我正在寻求一个尽可能便携的解决方案,所以我想尽可能避免系统调用。 evutil_socketpair 应该创建一个类似的解决方案,我承认我从未尝试过。

以上是关于如何通知运行 libevent 的线程它应该采取一些措施?的主要内容,如果未能解决你的问题,请参考以下文章

如何打破 libevent 的调度循环

带有 libevent 的多线程 HTTP 服务器

libevent 是不是允许在不同的线程中运行定时器/io 的回调?

关于Java线程超时退出的问题.

一起读读libevent的源代码:Libevent 第二章 创建Event base

在锁定屏幕上保留通知