2010-06-03 17 views
5

¿existen?C++ plantilla de objeto sin bloqueo

* añade para aclarar:

¿hay alguna biblioteca utilizable que implementar (que es multi-hilo y podría ser implementar spinlock u otro sincronización ligera) ObjectPool (http://en.wikipedia.org/wiki/Object_pool_pattern) escrito en lenguaje C sin bloqueo ++ usando la plantilla?

+2

¿Qué tipo de preguntas puedo hacer aquí? ¡Preguntas de programación, por supuesto! Mientras que su pregunta es: detallada y específica escrito con claridad y sencillez de interés para otros programadores - Trate de pasar un poco más de tiempo en su pregunta para que podamos realmente ser capaz de responder a ella. –

+0

¿por qué no está claro? ¿hay alguna biblioteca útil que implemente sin bloqueos (que sea segura para los hilos y podría implementar spinlock u otra sincronización liviana) ObjectPool (http://en.wikipedia.org/wiki/Object_pool_pattern) escrita en lenguaje C++ utilizando una plantilla? – uray

+0

Mi punto es que incluyó aproximadamente tres veces más información en su comentario que en su pregunta, y creo que debería ser al revés. –

Respuesta

3

terminé de escribir mi propia agrupación de objetos, su hilo de seguridad, bloqueo libre y multi-core escalable, como punto de referencia:

que podría hacer 16,6 millones de operaciones de préstamo de retención por segundo en el procesador Intel Core 2 Quad 2.4 GHz Win7 x64 usando 4 hilos

`

#define CACHE_LINE_SIZE 64 
#define alignCache __declspec(align(CACHE_LINE_SIZE)) 
#ifdef _WIN64 
# define alignArch __declspec(align(8)) 
#else 
# define alignArch __declspec(align(4)) 
#endif 

class InterlockedFlag { 
    protected: 
     alignArch volatile unsigned int value; 
    public: 
     inline void set(unsigned int val) { 
      this->value = val; 
     } 
     inline unsigned int exchange(unsigned int val) { 
      return InterlockedExchange(&this->value,val); 
     } 
}; 

#pragma pack(push,1) 
template <typename T> struct ObjectPoolNode { 
    ObjectPoolNode<T>* next; 
    T data; 
    ObjectPoolNode() : next(nullptr) { }; 
}; 
#pragma pack(pop,1) 

template <typename T> struct alignCache ObjectPoolList { 
    ObjectPoolList<T>* nextList; 
    char pad1[CACHE_LINE_SIZE - sizeof(ObjectPoolList<T>*)]; 
    ObjectPoolNode<T>* first; 
    char pad2[CACHE_LINE_SIZE - sizeof(ObjectPoolNode<T>*)]; 
    InterlockedFlag consumerLock; 
    char pad3[CACHE_LINE_SIZE - sizeof(InterlockedFlag)]; 
    ObjectPoolNode<T>* last; 
    char pad4[CACHE_LINE_SIZE - sizeof(ObjectPoolNode<T>*)]; 
    InterlockedFlag producerLock; 
    char pad5[CACHE_LINE_SIZE - sizeof(InterlockedFlag)]; 
    ObjectPoolNode<T>** storage;     
    char pad6[CACHE_LINE_SIZE - sizeof(ObjectPoolNode<T>**)]; 
    size_t available; 
    size_t count; 

    ObjectPoolList(size_t count) 
     : producerLock(false), consumerLock(false) 
    { 
     this->available = this->count = count; 
     this->storage = new ObjectPoolNode<T>*[count+1]; 
     for(size_t i=0 ; i<count+1 ; i++) { 
      this->storage[i] = new ObjectPoolNode<T>; 
     } 
     for(size_t i=0 ; i<count ; i++) { 
      this->storage[i]->next = this->storage[i+1]; 
     } 
     this->first = this->storage[0]; 
     this->last = this->storage[count];   
    } 

    ~ObjectPoolList() { 
     this->count = 0; 
     this->available = 0; 
     if(this->storage) { 
      for(size_t i=0 ; i<count+1 ; i++) { 
       delete this->storage[i]; 
      } 
      delete[] this->storage; 
      this->storage = NULL; 
     } 
    } 
}; 

template <typename T> class alignCache ObjectPool { 
private: 
    ObjectPoolList<T>** lists; 
    char pad1[CACHE_LINE_SIZE - sizeof(ObjectPoolList<T>**)]; 
    size_t available; 
    size_t listCount; 
public: 
    ObjectPool(size_t count,size_t parallelCount = 0) { 
     this->available = count; 
     this->listCount = parallelCount; 
     if(this->listCount == 0) { 
      this->listCount = getSystemLogicalProcessor(); //default 
     }  
     this->lists = new ObjectPoolList<T>*[this->listCount]; 
     for(size_t i=0 ; i<this->listCount ; i++) { 
      this->lists[i] = new ObjectPoolList<T>(count/this->listCount); 
     } 
     for(size_t i=0 ; i<this->listCount-1 ; i++) { 
      this->lists[i]->nextList = this->lists[i+1]; 
     } 
     this->lists[this->listCount-1]->nextList = this->lists[0]; 
    } 

    ~ObjectPool() { 
     if(this->lists) { 
      for(size_t i=0 ; i<this->listCount ; i++) { 
       delete this->lists[i]; 
      } 
      delete[] this->lists; 
      this->lists = NULL; 
     } 
     this->available = 0; 
     this->listCount = 0; 
    } 

    T* borrowObj() { 
     ObjectPoolList<T>* list = this->lists[0]; 
     while(!list->available || list->consumerLock.exchange(true)) { 
      if(!this->available) { 
       return NULL; 
      } 
      list = list->nextList; 
     } 
     if(list->first->next) { 
      ObjectPoolNode<T>* usedNode = list->first; 
      list->first = list->first->next; 
      list->available--; 
      this->available--; 
      list->consumerLock.set(false); 
      usedNode->next = nullptr; 
      return &usedNode->data;      
     }   
     list->consumerLock.set(false); 
     return NULL; 
    } 

    void returnObj(T* object) { 
     ObjectPoolNode<T>* node = (ObjectPoolNode<T>*)(((char*)object) - sizeof(ObjectPoolNode<T>*)); 
     ObjectPoolList<T>* list = this->lists[0]; 
     while(list->producerLock.exchange(true)) { 
      list = list->nextList; 
     } 
     list->last->next = node; 
     list->last  = node; 
     list->producerLock.set(false); 
     list->available++; 
     this->available++; 
    } 
}; 

`

+0

Parece específico de Windows ('InterlockExchange' es revelador), ¿es así? –

+0

no está restableciendo la cuenta a 0 en ~ ObjectPoolList() antes de que ese ciclo elimine los elementos de almacenamiento incorrectamente? – vavan

+0

no debería "this-> available--" y "this-> available ++" ser atómico (interbloqueado)? – vavan

1

Su mejor opción sería verificar Boost.Pool, y escribir una interfaz asignadora/mutex libre de bloqueo para ello.

+1

si utilizo boost :: pool y simplemente reemplazo su asignador con un asignador libre de bloqueos, creo que no hace que el grupo esté libre de bloqueos o de seguridad de eventos, ya que el impulso :: El grupo puede estar implementando una lista de fragmentos utilizando una lista enlazada o algo que no es seguro para subprocesos y no necesita sincronización en el asignador pero en el método de borrowReusable() y returnReusable(), entonces no sería un grupo libre de bloqueos, am ¿Verdad? – uray

0

Dado que hay colas sin bloqueo, diría que si el grupo no existe, seguramente puede crear un grupo (casi) sin bloqueo.

Combinado con el asignador tmalloc clásico (que puede bloquear pero lo evita tanto como sea posible), creo que se estaría acercando a su objetivo.

+1

mi preocupación es, si es realmente fácil o casi sin bloqueos con alto rendimiento, ¿por qué después de estos años de multinúcleo, nadie ya lo implementó ?. uno de mi caso, ya cuestionado aquí: http: //stackoverflow.com/questions/2953554/recycle-freed-objects. Estoy implementando la cola libre de bloqueos, pero quiero optimizarlo un poco, reduciendo la asignación de heap usando ObjectPool. pero habría algún beneficio de rendimiento, si el "conjunto de nodos de cola" de una cola se implementa utilizando la cola – uray

+0

Hum, que parece bastante redundante: p Me temo que no funcionaría entonces. –