2015-09-06 8 views
1

Я использую libevent в backend-потоке, чтобы запустить hiredis и подписаться на удаленную базу данных redis. Подписка работает великолепно, используя простые примеры из другого SO вопроса:Как я могу сообщить потоку, выполняющему libevent, что он должен предпринять какие-то действия?

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 мс времени, необходимого для подписки на событие. Это может быть значительным, или это может быть не так, но в любом случае это пустая трата времени.

Есть ли решения этой проблемы, которые будут решать эти проблемы в пределах libevent 1.4.x?

+0

Не уверен, что libevent использует pthreads? Если да, можете ли вы использовать сигнал, например. SIGHUP и установите сигмас pthread, чтобы только ваш backend-поток принимал сигнал? –

ответ

0

Лично я предпочитаю, чтобы целевой поток добавлял в свою очередь событий eventfd (или аналогичную конструкцию).

eventfd может быть уведомлен из любой другой темы безопасно и заставить целевой поток вызывать связанный обработчик событий.

Таким образом, вам не нужно беспокоиться о правильной блокировке абсолютного минимума структур libevent, поскольку ОС позаботится об этом для вас.

Примечание: eventfd не доступен на OSX, но его можно легко эмулировать трубой, если вы не требуете чрезвычайно высокой скорости событий.

+0

Это похоже на то, что мне нужно. Я предполагаю, что мне нужно будет использовать что-то вроде 'event_set (& event, fd, EV_READ, fn, data)', но как я: a) получить 'fd' и b)« touch »fd для запуска события? – arman

+0

Я должен уточнить; Я получаю решение, максимально возможное, поэтому, если возможно, мне бы хотелось избежать системных вызовов. – arman

+0

Существует 'evutil_socketpair', который должен создать аналогичное решение, которое я, по общему признанию, никогда не пробовал. –