2011-07-28 11 views
7

Como parte de un proyecto estoy escribiendo una función de registrador. Esta función de registrador envía un correo electrónico cuando el programa quiere registrar algo. Dado que sucedió que el servidor SMTP no respondía, he decidido enviar los correos en un hilo separado. Este hilo lee los mensajes de un std :: deque que se llena con la función de registro. El hilo se configura como sigue:Aserción en mutex al usar múltiples hilos y mutexes

while (!boost::this_thread::interruption_requested()) 
{ 
    EmailItem emailItem; 
    { 
    boost::unique_lock<boost::mutex> lock(mMutex); 
    while (mEmailBuffer.empty()) 
     mCond.wait(lock); 

    bufferOverflow = mBufferOverflow; 
    mBufferOverflow = false; 
    nrOfItems = mEmailBuffer.size(); 

    if (nrOfItems > 0) 
    { 
     emailItem = mEmailBuffer.front(); 
     mEmailBuffer.pop_front(); 
    } 
    } 

    if (nrOfItems > 0) 
    { 
     bool sent = false; 
     while(!sent) 
     { 
      try 
      { 
      ..... Do something with the message ..... 
      { 
       boost::this_thread::disable_interruption di; 
       boost::lock_guard<boost::mutex> lock(mLoggerMutex); 
       mLogFile << emailItem.mMessage << std::endl; 
      } 
      sent = true; 
      } 
      catch (const std::exception &e) 
      { 
      // Unable to send mail, an exception occurred. Retry sending it after some time 
      sent = false; 
      boost::this_thread::sleep(boost::posix_time::seconds(LOG_WAITBEFORE_RETRY)); 
      } 
     } 
    } 
} 

La función log() añade un nuevo mensaje a la cola de doble extremo (mEmailBuffer) de la siguiente manera:

{ 
    boost::lock_guard<boost::mutex> lock(mMutex); 
    mEmailBuffer.push_back(e); 
    mCond.notify_one(); 
} 

Cuando las salidas principales del programa, el destructor de la se llama objeto logger. Aquí es donde las cosas van mal, la aplicación se bloquea con un error:

/usr/include/boost/thread/pthread/mutex.hpp:45: boost::mutex::~mutex(): Assertion `!pthread_mutex_destroy(&m)' failed. 

El destructor simplemente llama a una interrupción en el hilo y luego se une a ella:

mQueueThread.interrupt(); 
mQueueThread.join(); 

En el programa principal, el uso múltiple diferentes clases que hacen uso de boost threading y mutex también, ¿podría esto causar este comportamiento? Al no llamar al destructor del objeto logger, no se producen errores, como ocurre con el uso del objeto logger y sin hacer nada más.

Supongo que estoy haciendo algo muy incorrecto, o hay un error en la biblioteca de subprocesos al usar varios subprocesos divididos en varias clases. ¿Alguien tiene una idea de cuál podría ser el motivo de este error?

EDITAR: Hice como @Andy T propuse y quité el código tanto como sea posible. Eliminé casi todo en la función que se ejecutó en un hilo diferente. El hilo ahora se ve así:

void Vi::Logger::ThreadedQueue() 
{ 
    bool bufferOverflow = false; 
    time_t last_overflow = 0; 
    unsigned int nrOfItems = 0; 

    while (!boost::this_thread::interruption_requested()) 
    { 
    EmailItem emailItem; 
    // Check for new log entries 
    { 
     boost::unique_lock<boost::mutex> lock(mMutex); 
     while (mEmailBuffer.empty()) 
     mCond.wait(lock); 
    } 
    } 
} 

El problema persiste. Vuelta atrás del problema, sin embargo me mostró algo diferente del código inicial:

#0 0x00007ffff53e9ba5 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
#1 0x00007ffff53ed6b0 in abort() at abort.c:92 
#2 0x00007ffff53e2a71 in __assert_fail (assertion=0x7ffff7bb6407 "!pthread_mutex_lock(&m)", file=<value optimized out>, line=50, function=0x7ffff7bb7130 "void boost::mutex::lock()") at assert.c:81 
#3 0x00007ffff7b930f3 in boost::mutex::lock (this=0x7fffe2c1b0b8) at /usr/include/boost/thread/pthread/mutex.hpp:50 
#4 0x00007ffff7b9596c in boost::unique_lock<boost::mutex>::lock (this=0x7fffe48b3b40) at /usr/include/boost/thread/locks.hpp:349 
#5 0x00007ffff7b958db in boost::unique_lock<boost::mutex>::unique_lock (this=0x7fffe48b3b40, m_=...) at /usr/include/boost/thread/locks.hpp:227 
#6 0x00007ffff6ac2bb7 in Vi::Logger::ThreadedQueue (this=0x7fffe2c1ade0) at /data/repos_ViNotion/stdcomp/Logging/trunk/src/Logger.cpp:198 
#7 0x00007ffff6acf2b2 in boost::_mfi::mf0<void, Vi::Logger>::operator() (this=0x7fffe2c1d890, p=0x7fffe2c1ade0) at /usr/include/boost/bind/mem_fn_template.hpp:49 
#8 0x00007ffff6acf222 in boost::_bi::list1<boost::_bi::value<Vi::Logger*> >::operator()<boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list0> (this=0x7fffe2c1d8a0, f=..., a=...) at /usr/include/boost/bind/bind.hpp:253 
#9 0x00007ffff6acf1bd in boost::_bi::bind_t<void, boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list1<boost::_bi::value<Vi::Logger*> > >::operator() (this=0x7fffe2c1d890) at /usr/include/boost/bind/bind_template.hpp:20 
#10 0x00007ffff6aceff2 in boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list1<boost::_bi::value<Vi::Logger*> > > >::run (this=0x7fffe2c1d760) 
    at /usr/include/boost/thread/detail/thread.hpp:56 
#11 0x00007ffff2cc5230 in thread_proxy() from /usr/lib/libboost_thread.so.1.42.0 
#12 0x00007ffff4d87971 in start_thread (arg=<value optimized out>) at pthread_create.c:304 
#13 0x00007ffff549c92d in clone() at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112 
#14 0x0000000000000000 in ??() 

Podría ser posible que mMutex no se desbloquea en la combinación del uso de un unique_lock() y luego interrumpir el hilo?

Respuesta

3

¿se une a su hilo antes de salir? como se sugirió tyz, su hilo aún puede mantenerlo bloqueado cuando se destruye mutex.

[EDIT]

no proporcionó ejemplo completo que puede ser compilada y ejecutada, es difícil para ayudar w/o ella.

cheque este sencillo ejemplo que debe ser similar a su ser:

#include <boost/thread.hpp> 
#include <boost/bind.hpp> 
#include <queue> 

class Test 
{ 
public: 
    Test() 
    { 
     thread = boost::thread(boost::bind(&Test::thread_func, this)); 
    } 

    ~Test() 
    { 
     thread.interrupt(); 
     thread.join(); 
    } 

    void run() 
    { 
     for (size_t i = 0; i != 10000; ++i) { 
      boost::lock_guard<boost::mutex> lock(mutex); 
      queue.push(i); 
      condition_var.notify_one(); 
     } 
    } 

private: 
    void thread_func() 
    { 
     while (!boost::this_thread::interruption_requested()) 
     { 
      { 
       boost::unique_lock<boost::mutex> lock(mutex); 
       while (queue.empty()) 
        condition_var.wait(lock); 
       queue.pop(); 
      } 
     } 
    } 

private: 
    boost::thread thread; 
    boost::mutex mutex; 
    boost::condition_variable condition_var; 
    std::queue<int> queue; 
}; 

int main() 
{ 
    Test test; 
    test.run(); 

    return 0; 
} 

compara con su caso

+0

Sí, uní el hilo antes de salir. Además, hasta donde yo sé, no hay puntos de interrupción en lugares donde un mutex está bloqueado en el hilo. Por lo tanto, no esperaría que el hilo todavía tenga un mutex bloqueado cuando se interrumpa. – Tim

+0

vuelva a verificar una vez más que su mutex (correcto) no se elimina antes de que se una a –

+0

Ambos mutexes son variables miembro de la clase Logger. El destructor de esta clase llama a las funciones 'interrupt()' y 'join()' en el hilo. Los mutexes, por lo tanto, todavía deberían estar vivos cuando el hilo se una, si estoy en lo cierto. – Tim

1

Debe desbloquear el mutex antes de eliminarlo.

+0

Gracias por su respuesta. En mi código, solo uso bloqueos de ámbito para bloquear los mutexes. Por lo tanto, deben desbloquearse automáticamente ya que el alcance está cerrado, ¿no es así? – Tim

+0

@Tim, lo intenté duro pero no encontré ningún error dentro del código dado.Aún así, el error que recibe generalmente se produce al eliminar el mutex bloqueado. –

+0

Agregué el desbloqueo explícito de los mutexes al final de la función, desafortunadamente sin éxito: mMutex.unlock(); mLoggerMutex.unlock(); – Tim