Cómo se implementan especialmente en caso de pthreads. ¿Qué API de sincronización pthread
usan bajo el capó? Un poquito de pseudocódigo sería apreciado.¿Cómo se implementan los bloqueos de lectura/escritura en pthread?
Respuesta
No he hecho ninguna programación pthreads por un tiempo, pero cuando lo hice, nunca utilicé bloqueos POSIX de lectura/escritura. El problema es que la mayoría de las veces un mutex será suficiente: es decir. su sección crítica es pequeña, y la región no es tan crítica para el rendimiento que valga la pena preocuparse por la doble barrera.
En aquellos casos donde el rendimiento es un problema, normalmente usar operaciones atómicas (generalmente disponibles como una extensión de compilador) es una mejor opción (es decir, la barrera adicional es el problema, no el tamaño de la sección crítica).
En el momento en que eliminas todos estos casos, te quedan casos en los que tienes requisitos específicos de rendimiento/equidad/rw-bias que requieren un verdadero rw-lock; y es entonces cuando descubres que todos los parámetros relevantes de rendimiento/equidad de POSIX rw-lock no están definidos y son específicos de la implementación. En este punto, en general es mejor que implemente el suyo para poder garantizar que se cumplan los requisitos adecuados de imparcialidad/rw-bias.
El algoritmo básico es contar cuántos de cada uno se encuentran en la sección crítica, y si todavía no se permite el acceso a un subproceso, desviarlo a una cola adecuada para esperar. La mayor parte de su esfuerzo consistirá en implementar la imparcialidad/parcialidad adecuada entre el servicio de las dos colas.
El siguiente pseudocódigo parecido a C, tipo pthreads, ilustra lo que trato de decir.
struct rwlock {
mutex admin; // used to serialize access to other admin fields, NOT the critical section.
int count; // threads in critical section +ve for readers, -ve for writers.
fifoDequeue dequeue; // acts like a cond_var with fifo behaviour and both append and prepend operations.
void *data; // represents the data covered by the critical section.
}
void read(struct rwlock *rw, void (*readAction)(void *)) {
lock(rw->admin);
if (rw->count < 0) {
append(rw->dequeue, rw->admin);
}
while (rw->count < 0) {
prepend(rw->dequeue, rw->admin); // Used to avoid starvation.
}
rw->count++;
// Wake the new head of the dequeue, which may be a reader.
// If it is a writer it will put itself back on the head of the queue and wait for us to exit.
signal(rw->dequeue);
unlock(rw->admin);
readAction(rw->data);
lock(rw->admin);
rw->count--;
signal(rw->dequeue); // Wake the new head of the dequeue, which is probably a writer.
unlock(rw->admin);
}
void write(struct rwlock *rw, void *(*writeAction)(void *)) {
lock(rw->admin);
if (rw->count != 0) {
append(rw->dequeue, rw->admin);
}
while (rw->count != 0) {
prepend(rw->dequeue, rw->admin);
}
rw->count--;
// As we only allow one writer in at a time, we don't bother signaling here.
unlock(rw->admin);
// NOTE: This is the critical section, but it is not covered by the mutex!
// The critical section is rather, covered by the rw-lock itself.
rw->data = writeAction(rw->data);
lock(rw->admin);
rw->count++;
signal(rw->dequeue);
unlock(rw->admin);
}
Algo así como el código anterior es un punto de partida para cualquier implementación de rwlock. Reflexione sobre la naturaleza de su problema y reemplace el dequeue con la lógica apropiada que determina qué clase de hilo se debe activar a continuación. Es común permitir que un número/período limitado de lectores supere a los escritores o viceversa según la aplicación.
Por supuesto, mi preferencia general es evitar rw-locks por completo; en general, mediante el uso de alguna combinación de operaciones atómicas, mutexes, STM, paso de mensajes y estructuras de datos persistentes. Sin embargo, hay momentos en que lo que realmente necesitas es un rw-lock, y cuando lo haces es útil saber cómo funcionan, así que espero que esto te haya ayudado.
EDITAR - En respuesta a la pregunta (muy razonable), ¿por dónde esperar en el pseudo-código:
he asumido que la aplicación quitar de la cola contiene la espera, por lo que en algún lugar dentro de append(dequeue, mutex)
o prepend(dequeue, mutex)
existe es un bloque de código a lo largo de las líneas de:
while(!readyToLeaveQueue()) {
wait(dequeue->cond_var, mutex);
}
que era por eso que pasé en la exclusión mutua correspondiente a las operaciones de la cola.
¿Puede indicar una explicación más detallada de por qué y cómo evitar los bloqueos de RW? He estado usando bloqueos pthread RW en lugares donde siento que no debería estar y me gustaría saber más acerca de lo que está hablando. – puffadder
No se trata de evitar bloqueos RW. Como dije en mi respuesta, cuando necesitas un bloqueo RW debes usar uno. El problema es que es raro necesitar un bloqueo RW y no tener requisitos de imparcialidad/imparcialidad. Los bloqueos de Posix RW no ofrecen garantías de imparcialidad/imparcialidad y, como tales, son, irónicamente, no portátiles para aquellos momentos en los que desee utilizarlos. Dado que no es difícil implementar su propio bloqueo RW portátil, también puede hacerlo. – Recurse
¿Dónde esperas las señales en el código? Veo señal (rw-> deque) en varios lugares, pero no hay código para esperar esa señal. – pythonic
Cada implementación puede ser diferente, pero normalmente tienen que favorecer a los lectores de forma predeterminada debido al requisito de POSIX de que un hilo pueda obtener el bloqueo de lectura en un rwlock varias veces. Si favorecían a los escritores, cada vez que un escritor estaba esperando, el lector se trababa en el segundo intento de bloqueo de lectura a menos que la implementación determinara que el lector ya tiene un bloqueo de lectura, pero la única forma de determinar eso es almacenar una lista de todos los hilos que contienen bloqueos de lectura, lo cual es muy ineficiente en cuanto a requisitos de tiempo y espacio.
Su comentario solo se aplica a reentrant rw-locks. Como se demostró en mi respuesta, las cerraduras no reentrantes solo requieren un conteo. Además, una lista es una opción muy pobre de estructura de datos para implementar la reentrada. Una combinación consciente de la caché de matrices cortas, mapas de bits y tablas hash lineales puede hacer que la contabilidad sea mucho más eficiente en espacio y tiempo. – Recurse
¿Por reentrant te refieres a recursivo? La reentrada es un requisito mucho más sólido que el bloqueo recursivo. –
- 1. ¿Cómo se implementan los C# Generics?
- 2. javascript internals: cómo se implementan los eventos?
- 3. ¿Cómo se implementan los analizadores DOM?
- 4. ¿Cómo se implementan los bloques try/catch?
- 5. ¿Cómo se implementan los cambios en el nivel de hardware?
- 6. ¿Cómo se implementan las clases en los compiladores
- 7. ¿Cómo se implementan los generadores y coroutines en CPython?
- 8. ¿Cómo se implementan los argumentos variables en gcc?
- 9. ¿Cómo se implementan los trabajos cron en producción?
- 10. ¿Cómo se implementan los métodos, `classmethod` y` staticmethod` en Python?
- 11. ¿Cómo implementan los structs los genéricos?
- 12. ¿Cómo se implementan las matrices en Perl?
- 13. ¿Cómo se implementan las enumeraciones en Java?
- 14. ¿Cómo se implementan las matrices en Java?
- 15. conceptos pthread en Linux
- 16. ¿Cuándo se lanzan los bloqueos de lectura compartidos?
- 17. ¿Cómo manejas los bloqueos en tus aplicaciones de iPhone?
- 18. ¿Cómo se implementan las referencias débiles?
- 19. Cómo obtener pid de pthread
- 20. Cómo se implementan las funciones de la biblioteca en Haskell
- 21. ¿Cómo se implementan las consolas de depuración en Python?
- 22. ¿Cómo funciona pthread?
- 23. ¿Cómo implementan JVM IdentityHashMap?
- 24. ¿Se implementan Markov Random Fields en OpenCV?
- 25. ¿Cómo se implementa pthread en Linux kernel 3.2?
- 26. mysql - ¿los bloqueos se propagan sobre la replicación?
- 27. Eliminación de los miembros que implementan IDisposable
- 28. ¿Qué son los bloqueos de rango?
- 29. ¿Por qué no se implementan los mapas C++ como intentos?
- 30. ¿Cómo se implementan las categorías en el Objetivo C?
¿podría leer los códigos? – tbert
Tal vez [esto] (http: //www.cognitus.net/html/howto/pthreadSemiFAQ_10.html) puede ayudar? –