Para un buffer circular tradicional de un solo bloque Creo que esto simplemente no se puede hacer de manera segura con operaciones atómicas. Necesita hacer tanto en una lectura. Suponga que tiene una estructura que tiene esta:
uint8_t* buf;
unsigned int size; // Actual max. buffer size
unsigned int length; // Actual stored data length (suppose in write prohibited from being > size)
unsigned int offset; // Start of current stored data
En una lectura que hay que hacer lo siguiente (así es como he implementado todas maneras, puede intercambiar algunas medidas como voy a discutir después):
- Comprobar si la longitud de lectura no superan almacena longitud
- Comprobar si el desplazamiento + leer longitud no supere los límites de amortiguamiento
- datos leídos
- Aumentar el offset, disminuir la longitud
¿Qué debe hacer sincronizado (tan atómico) para que funcione? En realidad combinar los pasos 1 y 4 en un solo paso atómico, o para aclarar: hacer esto sincronizado:
- cheque read_length, esto puede ser algo como
read_length=min(read_length,length);
- longitud disminución con read_length:
length-=read_length
- conseguir una copia local desde el desplazamiento
unsigned int local_offset = offset
- compensado con read_length aumento:
offset+=read_length
Después que sólo puede hacerlo un establecimiento de memoria (o w lo que sea) comenzando desde su local_offset, verifique si su lectura abarca el tamaño circular del búfer (dividido en 2 memcpy's), .... Esto es 'bastante' seguro, su método de escritura aún podría escribir sobre la memoria que está leyendo, así que asegúrese de que su memoria intermedia sea lo suficientemente grande como para minimizar esa posibilidad.
Ahora, aunque puedo imaginar que puedes combinar 3 y 4 (supongo que eso es lo que hacen en el caso de lista enlazada) o incluso 1 y 2 en operaciones atómicas, no veo que hagas todo esto en un atómico operación :).
Sin embargo, puede intentar eliminar la verificación de "longitud" si sus consumidores son muy inteligentes y siempre sabrán qué leer. También necesitaría una nueva variable de woffset, porque el antiguo método de (desplazamiento + longitud)% de tamaño para determinar el desplazamiento de escritura ya no funcionaría. Tenga en cuenta que esto está cerca del caso de una lista vinculada, donde en realidad siempre lee un elemento (= tamaño fijo, conocido) de la lista.También aquí, si lo convierte en una lista circular, puede leer mucho o escribir en un puesto que esté leyendo en ese momento.
Finalmente: mi consejo, solo use bloqueos, uso una clase CircularBuffer, completamente segura para leer & escribiendo) para un transmisor de video en tiempo real 720p60 y no tengo problemas de velocidad desde el bloqueo.
búfer de tamaño limitado significa que el productor puede fallar si no hay espacio vacío en él. ¿Eso es aceptable para ti? – doublep
También tenga en cuenta que en C++ puede suministrar su propio asignador a 'std :: list'. Como solo tiene un productor, este asignador no necesita sincronizarse. Por ejemplo, puede "asignar" nodos de lista desde un búfer preasignado y, cuando se quede sin espacio, asignar un nuevo búfer con el asignador "real" malloc() 'similar a" global "sincronizado. Lo que significa que utilizará la sincronización en, por ejemplo, el 1% de las llamadas solamente. – doublep
tcmalloc es una gran biblioteca para mirar si está buscando optimizar el uso de la memoria para los hilos. Como mantiene grupos de memoria para cada subproceso, probablemente evite el problema de serialización de la rutina de memoria. –