2009-05-16 14 views
5

Tengo una pregunta bastante específica sobre la programación simultánea en C. He hecho bastante investigación sobre esto pero he visto varias respuestas conflictivas, así que espero alguna aclaración. Tengo un programa que es algo así como lo siguiente (lo siento por el bloque de código bastante largo):acceso variable concurrente en c

typedef struct { 
    pthread_mutex_t mutex; 
    /* some shared data */ 
    int eventCounter; 
} SharedData; 

SharedData globalSharedData; 

typedef struct { 
    /* details unimportant */ 
} NewData; 

void newData(NewData data) { 
    int localCopyOfCounter; 

    if (/* information contained in new data triggers an 
     event */) { 
    pthread_mutex_lock(&globalSharedData.mutex); 
    localCopyOfCounter = ++globalSharedData.eventCounter; 
    pthread_mutex_unlock(&globalSharedData.mutex); 
    } 
    else { 
    return; 
    } 

    /* Perform long running computation. */ 

    if (localCopyOfCounter != globalSharedData.eventCounter) { 
    /* A new event has happened, old information is stale and 
     the current computation can be aborted. */ 
    return; 
    } 

    /* Perform another long running computation whose results 
    depend on the previous one. */ 

    if (localCopyOfCounter != globalSharedData.eventCounter) { 
    /* Another check for new event that causes information 
     to be stale. */ 
    return; 
    } 

    /* Final stage of computation whose results depend on two 
    previous stages. */ 
} 

Hay un grupo de subprocesos que sirven a la conexión de datos de entrada, por lo que varias instancias de newData puede estar en ejecución al mismo tiempo, . En un entorno multiprocesador, conozco dos problemas para corregir el código de este código: evitar que el compilador guarde en caché la copia del contador compartido en un registro para que otros subprocesos no puedan verlo y forzar CPU para escribir el almacenamiento del valor del contador en la memoria de manera oportuna para que otros subprocesos puedan verlo. Preferiría no utilizar una llamada de sincronización alrededor de las comprobaciones del contador porque una lectura parcial del valor del contador es aceptable (producirá un valor diferente al de la copia local, que debería ser adecuado para concluir que se ha producido un evento). ¿Sería suficiente declarar el campo eventCounter en SharedData como volátil, o tengo que hacer algo más aquí? También hay una mejor manera de manejar esto?

Respuesta

2

Desafortunadamente, el estándar C dice muy poco acerca de la concurrencia. Sin embargo, la mayoría de los compiladores (gcc y msvc, de todos modos) considerarán una lectura volátil como si tuviera acquire semantics - la variable volátil se volverá a cargar desde la memoria en cada acceso. Eso es deseable, su código tal como está ahora puede terminar comparando valores almacenados en caché en los registros. Ni siquiera me sorprendería si las dos comparaciones fueran optimizadas.

Así que la respuesta es sí, haga que el eventCounter sea volátil. Alternativamente, si no quiere restringir demasiado su compilador, puede usar la siguiente función para realizar lecturas de eventCounter.

int load_acquire(volatile int * counter) { return *counter; } 

if (localCopy != load_acquire(&sharedCopy)) 
    // ... 
0

prevenir el compilador de almacenamiento en caché la copia contador local en un registro por lo demás hilos no puede ver que

Su copia contador local es "local", creado sobre la ejecución pila y visible solo para el hilo en ejecución. Cada otro hilo se ejecuta en una pila diferente y tiene la propia variable de contador local (sin concurrencia).

Su contador global debe declararse volátil para evitar la optimización del registro.

+0

Tiene toda la razón sobre la copia local, solo corrigí ese error en mi texto. – user98166

0

También puede usar el ensamblado codificado a mano o el compilador intrinsics que controlará las comprobaciones atómicas de su mutex, también pueden detectarse atómicamente ++ y - su contador.

volátil es useless En la actualidad, en su mayor parte, debe tener en cuenta la barrera de memoria que son otras instalaciones de CPU de bajo nivel para ayudar con la contención multi-core.

Sin embargo, el mejor consejo que puedo dar es que se dediquen a las diversas bibliotecas de soporte multi-core administradas y nativas. Creo que algunos de los más antiguos son como OpenMP o MPI (basado en mensajes), todavía están pateando y la gente seguirá hablando de lo geniales que son ... sin embargo, para la mayoría de los desarrolladores, algo como intel TBB o Microsoft's nuevas API, también acabo de cavar hasta este proyecto de código article, aparentemente está usando cmpxchg8b que es la ruta de hardware de bajo nivel que mencioné inicialmente ...

Buena suerte.

Cuestiones relacionadas