2010-05-31 11 views

Respuesta

10

Herb Sutter cubre sólo una cola, como parte de su columna Concurency eficaz en el Dr. Dobbs Journal.

Writing Lock-Free Code: A Corrected Queue

+0

que es la teoría, lo que I'am pidiendo es la implementación. ¿Hay algún código fuente o biblioteca que implemente esos algoritmos? – uray

+0

@uray: Sí. En el artículo. – greyfade

+0

¿dónde? no veo ningún archivo de código fuente allí. – uray

0

Si' Estamos buscando una buena implementación de cola libre de bloqueo tanto Microsoft Visual Studio 2010 & Los bloques de construcción de subprocesos de Intel contienen una buena cola LF que es similar al papel.

Here's a link to the one in VC 2010

+0

Hierbas, por supuesto, también está bien y corregir :) – Rick

+0

Probé el vs2010 uno y benchmarked, es más rápido que "std :: queue with lock" en pequeños conjuntos de datos, pero exponencialmente más lento en el gran conjunto de datos – uray

3

Quiero concluir la respuesta dada por greyfade, que se basa en http://www.drdobbs.com/high-performance-computing/212201163 (la última parte del artículo), el código optimizado sería (con algunas modificaciones para adaptarse a mi nombramiento y convenciones de codificación) : `

template <typename T> class LFQueue { 
private: 
    struct LFQNode { 
     LFQNode(T* val) : value(val), next(nullptr) { } 
     T* value; 
     AtomicPtr<LFQNode> next; 
     char pad[CACHE_LINE_SIZE - sizeof(T*) - sizeof(AtomicPtr<LFQNode>)]; 
    }; 

    char pad0[CACHE_LINE_SIZE]; 
    LFQNode* first;     // for one consumer at a time 
    char pad1[CACHE_LINE_SIZE - sizeof(LFQNode*)]; 
    InterlockedFlag consumerLock; // shared among consumers 
    char pad2[CACHE_LINE_SIZE - sizeof(InterlockedFlag)]; 
    LFQNode* last;     // for one producer at a time 
    char pad3[CACHE_LINE_SIZE - sizeof(LFQNode*)]; 
    InterlockedFlag producerLock; // shared among producers 
    char pad4[CACHE_LINE_SIZE - sizeof(InterlockedFlag)]; 
public: 
    LFQueue() { 
     first = last = new LFQNode(nullptr); // no more divider 
     producerLock = consumerLock = false; 
    } 

    ~LFQueue() { 
     while(first != nullptr) { 
      LFQNode* tmp = first; 
      first = tmp->next; 
      delete tmp; 
     } 
    } 

    bool pop(T& result) { 
     while(consumerLock.set(true)) 
     { }        // acquire exclusivity 
     if(first->next != nullptr) { // if queue is nonempty 
      LFQNode* oldFirst = first; 
      first = first->next; 
      T* value = first->value; // take it out 
      first->value = nullptr;  // of the Node 
      consumerLock = false;  // release exclusivity 
      result = *value;   // now copy it back 
      delete value;    // and clean up 
      delete oldFirst;   // both allocations 
      return true;    // and report success 
     } 
     consumerLock = false;   // release exclusivity 
     return false;     // queue was empty 
    } 

    bool push(const T& t) { 
     LFQNode* tmp = new LFQNode(t); // do work off to the side 
     while(producerLock.set(true)) 
     { }        // acquire exclusivity 
     last->next = tmp;    // A: publish the new item 
     last = tmp;      // B: not "last->next" 
     producerLock = false;   // release exclusivity 
     return true; 
    } 
}; 

`

otra pregunta es ¿cómo se define CACHE_LINE_SIZE? es diferente en CPU alguna vez, ¿verdad?

+0

Un buen número para elegir sería ser '64' bytes, creo. Pero es probable que desee equilibrarlo con el tamaño, por lo que le sugiero que mire las CPU de destino y elija un tamaño que se ajuste a las más comunes a las que espera apuntar. – greyfade

+1

Solo una nota rápida: este no es un foro, por lo que no se puede suponer que las personas "naveguen por el hilo". Si desea hacer otra pregunta, mejor use el campo "Preguntar" en lugar de "Su respuesta". –

+0

De hecho, estoy volviendo a responder la pregunta, pero me equivoqué al preguntar en el campo de respuesta, debería agregar un nuevo comentario debajo de mi propia respuesta. Lo siento por eso. – uray

1

Aquí está mi implementación de un FIFO sin bloqueo.

Asegúrese de que cada elemento de T sea un múltiplo de 64 bytes (el tamaño de la línea de caché en las CPU Intel) para evitar el uso compartido falso.

Este código se compila con gcc/mingw y debe compilarse con clang. Está optimizado para 64 bits, por lo que para que se ejecute en 32 bits necesitaría algunas refactorizaciones.

https://github.com/vovoid/vsxu/blob/master/engine/include/vsx_fifo.h

vsx_fifo<my_struct, 512> my_fifo; 

remitente:

my_struct my_struct_inst; 
... fill it out ... 
while (!my_fifo.produce(my_struct_inst)) {} 

receptor:

my_struct my_struct_recv; 
while(my_fifo.consume(my_struct_recv)) 
{ 
    ...do stuff... 
} 
+0

Como veo, es seguro para subprocesos pero no reentrante. – peterh

Cuestiones relacionadas