2011-08-19 17 views
5

Mi programa se bloquea al azar en un pequeño escenario que puedo reproducir, pero ocurre en mlock.c (que es un archivo de tiempo de ejecución de VC++) de ntdll.dll, y no puedo ver el rastro de pila Sin embargo, sé que sucede en una de mis funciones de subprocesos.VC++ 2010: error de la sección Weird Critical

Este es el código mlock.c donde el programa se bloquea:

void __cdecl _unlock (
     int locknum 
     ) 
{ 
     /* 
     * leave the critical section. 
     */ 
     LeaveCriticalSection(_locktable[locknum].lock); 
} 

el error es "identificador no válido especificado". Si miro a locknum, es un número más grande que el tamaño de la tabla de bloqueo, por lo que tiene sentido.

Esto parece estar relacionado con el uso de Sección crítica. Utilizo CRITICAL_SECTIONS en mi hilo, a través de una clase contenedora CCriticalSection y su guardia RAII asociada, CGuard. Las definiciones para ambos here para evitar aún más desorden.

Ésta es la función del hilo que está chocando:

unsigned int __stdcall CPlayBack::timerThread(void * pParams) { 
#ifdef _DEBUG 
    DRA::CommonCpp::SetThreadName(-1, "CPlayBack::timerThread"); 
#endif 
    CPlayBack * pThis = static_cast<CPlayBack*>(pParams); 
    bool bContinue = true; 
    while(bContinue) { 
     float m_fActualFrameRate = pThis->m_fFrameRate * pThis->m_fFrameRateMultiplier; 
     if(m_fActualFrameRate != 0 && pThis->m_bIsPlaying) { 
      bContinue = (::WaitForSingleObject(pThis->m_hEndThreadEvent, static_cast<DWORD>(1000.0f/m_fActualFrameRate)) == WAIT_TIMEOUT); 
      CImage img; 
      if(pThis->m_bIsPlaying && pThis->nextFrame(img)) 
       pThis->sendImage(img); 
     } 
     else 
      bContinue = (::WaitForSingleObject(pThis->m_hEndThreadEvent, 10) == WAIT_TIMEOUT); 
    } 
    ::GetErrorLoggerInstance()->Log(LOG_TYPE_NOTE, "CPlayBack", "timerThread", "Exiting thread"); 
    return 0; 
} 

dónde viene CCriticalSection entrar? Cada objeto CImage contiene un objeto CCriticalSection que utiliza a través de un bloqueo RAII CGuard. Además, cada CImage contiene un objeto CSharedMemory que implementa el recuento de referencias. Con ese fin, contiene dos CCriticalSection también, uno para los datos y otro para el contador de referencia. Un buen ejemplo de estas interacciones se ve mejor en los destructores:

CImage::~CImage() { 
    CGuard guard(m_csData); 
    if(m_pSharedMemory != NULL) { 
     m_pSharedMemory->decrementUse(); 
     if(!m_pSharedMemory->isBeingUsed()){ 
      delete m_pSharedMemory; 
      m_pSharedMemory = NULL; 
     } 
    } 
    m_cProperties.ClearMin(); 
    m_cProperties.ClearMax(); 
    m_cProperties.ClearMode(); 
} 

CSharedMemory::~CSharedMemory() { 
    CGuard guardUse(m_cs); 
    if(m_pData && m_bCanDelete){ 
     delete []m_pData; 
    } 
    m_use = 0; 
    m_pData = NULL; 
} 

¿Alguien se topó con este tipo de error? ¿Cualquier sugerencia?

Editar: Tengo que ver un montón de llamadas: la llamada proviene de ~ CSharedMemory. Así que tiene que haber alguna condición de carrera no

Editar: Código Más CSharedMemory here

+0

¿Daño en la memoria? –

+0

Las dos clases se ven bien. ¿Puedes mostrar algún código relacionado con CÓMO los estás usando? ¿Estás seguro de que los constructores se llaman correctamente antes del uso (no hay conflicto de hilos en los constructores?). ¿Están siendo asignados dinámicamente (por alguna razón)? – Chad

+0

Tu clase no tiene nada que ver con el código CRT, usa Windows. La depuración de las carreras de subprocesos y la corrupción de montón nunca es divertida, buena suerte con ella. –

Respuesta

1

me decidieron adherirse al principio KISS y rock and roll toda la noche simplificar las cosas. Pensé que reemplazaría el CSharedMemoryClass con un std::tr1::shared_ptr<BYTE> y un CCriticalSection que lo protegen del acceso concurrente. Ambos son miembros de CImage ahora, y las preocupaciones están mejor separadas ahora, en mi humilde opinión.

Eso solucionó la sección crítica extraña, pero ahora parece que tengo una pérdida de memoria causada por std::tr1::shared_ptr, puede que me vean publicar pronto ... ¡Nunca termina!

5

El "no válido identificador especificado" código de retorno pinta un cuadro bastante claro que el objeto de sección crítica se ha desasignado; asumiendo, por supuesto, que fue asignado correctamente para comenzar.

Su clase RAII parece ser un posible culpable. Si se toma un paso atrás y pensar en ello, la clase RAII viola el principio Sepration Of Concerns, ya que tiene dos trabajos:

  1. Proporciona asignar/destruir la semántica para el CRITICAL_SECTION
  2. Proporciona semántica adquieren/liberación de la la SESIÓN CRÍTICA

La mayoría de las implementaciones de un envoltorio de CS que he visto violan el principio de SoC de la misma manera, pero puede ser problemático. Especialmente cuando debe comenzar a pasar instancias de la clase para llegar a la funcionalidad de adquisición/liberación. Consideremos un ejemplo sencillo, ideada en psudocode:

void WorkerThreadProc(CCriticalSection cs) 
{ 
    cs.Enter(); 
    // MAGIC HAPPENS 
    cs.Leave(); 
} 

int main() 
{ 
    CCriticalSection my_cs; 
    std::vector<NeatStuff> stuff_used_by_multiple_threads; 

    // Create 3 threads, passing the entry point "WorkerThreadProc" 
    for(int i = 0; i < 3; ++i) 
    CreateThread(... &WorkerThreadProc, my_cs); 

    // Join the 3 threads... 
    wait(); 
} 

El problema aquí es CCriticalSection se pasa por valor, por lo que se llama al destructor 4 veces. Cada vez que se llama al destructor, CRITICAL_SECTION se desasigna. La primera vez funciona bien, pero ahora ya no está.

Puede pasar por alto este problema pasando referencias o punteros a la clase de sección crítica, pero luego enturbia las aguas semánticas con problemas de propiedad. ¿Qué pasa si el hilo que "posee" el crítico sec muere antes que los otros hilos? Podría usar un shared_ptr, pero ahora nadie realmente "posee" la sección crítica, y ha renunciado a un poco de control en el área para ganar un poco en otra área.

La verdadera "solución" para este problema es separar las preocupaciones. Tener una clase para la asignación & cancelación de asignación:

class CCriticalSection : public CRITICAL_SECTION 
{ 
public: 
    CCriticalSection(){ InitializeCriticalSection(this); } 
    ~CCriticalSection() { DestroyCriticalSection(this); } 
}; 

... y otro para controlar el bloqueo de desbloqueo & ...

class CSLock 
{ 
public: 
    CSLock(CRITICAL_SECTION& cs) : cs_(cs) { EnterCriticalSection(&cs_); } 
    ~CSLock() { LeaveCriticalSection(&cs_); } 
private: 
    CRITICAL_SECTION& cs_; 
}; 

Ahora se puede pasar alrededor de punteros primas o referencias a un único objeto CCriticalSection, posiblemente const, y los subprocesos de trabajo crean una instancia de sus propios CSLocks en él. CSLock es propiedad del subproceso que lo creó, que es como debería ser, pero la propiedad de CCriticalSection queda claramente retenida por algún hilo de control; también es algo bueno

+0

No veo cómo esto difiere de lo que hice antes, excepto que usa herencia en lugar de composición y llama a EnterCriticalSection y LeaveCriticalSection en el equivalente a mi clase CGuard . ¿Qué gano al hacerlo de esa manera? –

+0

Has cambiado tu pregunta hasta el punto de que ahora no sabemos cómo se ve CCriticalSection, qué es CGuard o cómo están relacionados. Usted dice cosas como "Cada objeto CImage contiene un objeto CCriticalSection que utiliza a través de un bloqueo CGuard RAII", pero a menos que veamos cómo se declaran e implementan, realmente no sabemos lo que eso significa. –

+0

Lo siento, eliminé ese código porque alguien me dijo que mis clases estaban bien y para evitar la confusión. Lo publiqué en Ideone.com, consulte el enlace en mi pregunta actualizada –

1
  • Asegúrese de que el objeto de la sección crítica no se encuentre en #pragma embalaje 1 (o en un embalaje no predeterminado).
  • Asegúrese de que ningún otro hilo (o el mismo hilo) esté dañando el objeto CS. Ejecute alguna herramienta de análisis estático para verificar si hay algún problema de desbordamiento del búfer.
  • Si tiene la herramienta de análisis de tiempo de ejecución, ejecútela para encontrar el problema.
Cuestiones relacionadas