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

时间:2015-09-06 07:57:47

标签: c++ c libevent

我在后端线程中使用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. 读者很难在没有广泛评论的情况下关注
  3. 很慢;此计时器将添加多达1毫秒的订阅事件所需的时间。这可能很重要,也可能不会,但无论哪种方式都浪费时间。
  4. 这个问题的解决方案是否会在libevent 1.4.x的范围内解决这些问题?

1 个答案:

答案 0 :(得分:1)

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

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

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

注意: public class logInController : Controller { [HttpPost] public ActionResult Index(logInModel model) { if(ModelState.IsValid) { int match = 0; sqlConn = new SqlConnection(sqlConnString); sqlComm = new SqlCommand("spLogin", sqlConn); sqlComm.CommandType = CommandType.StoredProcedure; sqlComm.Parameters.AddWithValue("@userName", model.Username); sqlComm.Parameters.AddWithValue("@password", model.Password); SqlParameter userMatch = new SqlParameter("@userMatch", SqlDbType.Int); userMatch.Direction = ParameterDirection.Output; sqlComm.Parameters.Add(userMatch); sqlConn.Open(); sqlComm.ExecuteNonQuery(); match = Convert.ToInt32(sqlComm.Parameters["@userMatch"].Value); sqlConn.Close(); if (match != 0) { FormsAuthentication.SetAuthCookie(model.Username, false); return RedirectToAction("index","home"); } else ModelState.AddModelError("", "Invalid username or password"); } return View(); } } 在OSX上不可用,但只要您不需要极高的事件率,就可以使用管道轻松模拟。