【问题标题】:How can I inform a thread running libevent that it should take some action?如何通知运行 libevent 的线程它应该采取一些措施?
【发布时间】: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 等功能,我必须在每个事件中重新创建计时器)。

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

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

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

【问题讨论】:

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

标签: c++ c libevent


【解决方案1】:

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

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

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

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

【讨论】:

  • 这听起来像我需要的。我猜我需要使用 event_set(&amp;event, fd, EV_READ, fn, data) 之类的东西,但是我该如何 a) 获取 fd 和 b) "touch" fd 来触发事件?
  • 我应该澄清一下;我正在寻求一个尽可能便携的解决方案,所以我想尽可能避免系统调用。
  • evutil_socketpair 应该创建一个类似的解决方案,我承认我从未尝试过。
猜你喜欢
  • 2014-05-11
  • 1970-01-01
  • 2010-11-24
  • 2018-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-19
  • 2018-06-09
相关资源
最近更新 更多