2009-12-03 19 views
5

¿Está seguro el hilo de std :: list? Supongo que no es así. Agregué mis propios mecanismos de sincronización (creo que tengo el término correcto). Pero todavía estoy teniendo problemasstd :: list threading push_back, front, pop_front

Cada función es llamada por una secuencia separada. Thread1 no puede esperar, tiene que ser lo más rápido posible

std::list<CFoo> g_buffer; 
bool g_buffer_lock; 

void thread1(CFoo frame) { 
    g_buffer_lock = true ; 
    g_buffer.push_back(frame) ; 
    g_buffer_lock = false; 
} 


void thread2() 
{ 
    while(g_buffer_lock) { 
     // Wait 
    } 

    // CMSTP_Send_Frame * pMSTPFrame = NULL ; 
    while (! g_buffer_lock && g_buffer.size() > 0) 
    { 
     // Get the top item 
     CFoo& pFoo = g_buffer.front() ; 

     // Do something. 

     // remove the front item 
     g_buffer.pop_front(); 
    } 
} 

Después de unos 170k llamadas a Thread1 y 900k llama a Thread2 me sale un error de excepción en CFoo& pFoo = g_buffer.front() ;

Eso hace que el programa se bloquee. stdthrow.cpp: 22

#ifdef _DEBUG 
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Debug_message(const wchar_t *message, const wchar_t *file, unsigned int line) 
    { // report error and die 
     if(::_CrtDbgReportW(_CRT_ASSERT, file, line, NULL, message)==1) 
     { 
      ::_CrtDbgBreak(); 
     } 
    } 
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Debug_message(const unsigned short *message, const unsigned short *file, unsigned int line) 
    { // report error and die 
     _Debug_message((wchar_t *) message, (wchar_t *) file, line); 
    } 

#endif 

sugerencias, comentarios, hay una mejor manera de hacer las cosas?

+4

¿Está seguro el hilo de std :: list? No. – KeatsPeeks

Respuesta

15

¿Está seguro el hilo de la lista estándar?

El estándar actual de C++ ni siquiera reconoce la existencia de hilos, por lo que std::list ciertamente no lo es. Diferentes implementaciones, sin embargo, pueden proporcionar (diferentes niveles de) seguridad de subprocesos.

En cuanto a su código: si necesita un candado, use un candado. Es posible que la variable bool no ayude cuando los subprocesos se ejecutan en diferentes núcleos que lo extraen de diferentes cachés. Use un mutex real en su lugar.

6

No, no se garantiza que sea seguro para subprocesos.

Su mecanismo de sincronización está defectuoso. Está permitiendo que thread1 cambie la lista, mientras que thread2 está trabajando con eso. Esto puede causar problemas. Además de eso, debe hacer su variable de bloqueo volatile.

+1

Cheques agregados con esperas para ambas funciones. Ejecutar pruebas para ver si resuelve este problema. Lo habría pensado ya que thread1 solo agrega elementos y thread2 solo elimina elementos que no causarían ningún problema. Pero parece que sí. –

+3

@Steven: como observó sbi en su respuesta, será mejor que utilices un mecanismo de bloqueo provisto por el sistema en lugar de tirar el tuyo. –

1

Tiene razón al suponer que no se garantiza que una lista stl sea segura para subprocesos.

También es probable que su mecanismo de sincronización no sea muy bueno.

Dentro de thread2 no hace bloqueo su bool, entonces usted tiene un problema allí.

Es probable que como mínimo ponga un calificador volátil delante de su bool de bloqueo; o mejor aún, busque funciones mutex reales apropiadas para su plataforma.

+6

volátil no reemplaza las barreras de memoria y no funcionará cuando se requiera una barrera de memoria real. –

1

El hecho de que Thread1 necesite ser lo más rápido posible, no significa que esté bien permitirle acceder a una lista mientras está siendo modificado por otra cadena. Como ambos hilos están modificando la lista, ambos tienen que esperar. Ser rápido no ayuda si terminas con datos corruptos.

Editar:

En realidad, usted puede salirse con la suya ... Thread1 sólo agrega elementos a la lista, mientras que Thread2 sólo les quita. Esto significa que Thread1 solo necesita esperar si la lista solo contiene un elemento.

Edit2:

Por lo tanto, la manera de hacer este trabajo es para thread2 para bloquear la lista si sólo contiene un elemento.Tendría que verificar eso antes de cada eliminación. De esta forma, thread1 no tendría que esperar, excepto en ese caso.

Y definitivamente debe utilizar un mecanismo de exclusión mutua adecuado (lo que está disponible en su plataforma) en lugar de un indicador booleano.

+1

Esto es lo que también pensé, porque thread1 agrega y thread2 elimina elementos que podría ejecutarlos al mismo tiempo ... pero en la práctica no parece ser el caso. –

+0

Eso es porque no sabes cuántos elementos elimina thread2 mientras thread1 está tratando de agregar ... – Dima

+1

Creo que lo tengo. thread2 solo debe bloquear la lista si está a punto de eliminar el último elemento. De esta forma, thread1 nunca tendrá que esperar, excepto en ese caso. – Dima

0

Si realmente necesita Thread1 a ser tan rápido como sea posible, pero todavía necesita hilo de seguridad, puede evitar algunos contención de bloqueo a costa de una cantidad mínima de sobrecarga, como tal:

std::list<CFoo> g_buffer_thread1; 
std::list<CFoo> g_buffer_thread2; 
Mutex g_mutex; 

void thread1(CFoo frame) { 
    Locker lock(g_mutex); 
    g_buffer_thread1.push_back(frame) ; 
} 


void thread2() 
{ 
    while(g_buffer_thread2.size() == 0) { 
     // Wait? 
     Locker lock(g_mutex); 
     g_buffer_thread1.swap(g_buffer_thread2); 
    } 

    while (g_buffer_thread2.size() > 0) 
    { 
     CFoo& pFoo = g_buffer_thread2.front() ; 
     // Do something. 
     g_buffer_thread2.pop_front(); 
    } 
} 

pienso esta es la combinación más directa con seguridad de hilo. Thread1 siempre debe bloquearse, desafortunadamente. Es posible que pueda encontrar algo en que agrupe marcos para thread1. Supongo, basado en sus números en la pregunta, que thread1 se ejecuta muchas más veces que thread2, por lo que guardará alguna contención de bloqueo que de otro modo ocurriría al usar solo una lista de buffer.

Cuestiones relacionadas