2011-11-16 15 views
7

Estoy buscando una forma de hacer un registro asíncrono y seguro para subprocesos en mi C++.Registro asíncrono seguro de subprocesos en C++ (sin mutex)

Ya he explorado soluciones de registro seguras de subprocesos como log4cpp, log4cxx, Boost: log o rlog, pero parece que todas usan un mutex. Y hasta donde sé, mutex es una solución sincrónica, lo que significa que todos los hilos están bloqueados mientras intentan escribir sus mensajes mientras que otros lo hacen.

¿Conoces una solución?

+0

Si no usa mutex al escribir en el archivo de registro, es posible que obtenga bloqueos o registros mixtos, porque sus accesos de escritura son simultáneos –

Respuesta

0

En un programa de Windows, utilizamos un mensaje de Windows definido por el usuario. Primero, la memoria se asigna para la entrada de registro en el montón. Luego se llama PostMessage, con el puntero como LPARAM y el tamaño de registro como WPARAM. La ventana del receptor extrae el registro, lo muestra y lo guarda en el archivo de registro. Luego, se devuelve PostMessage y el remitente desasigna la memoria asignada. Este enfoque es seguro para subprocesos y no es necesario usar mutexes. La concurrencia se maneja mediante el mecanismo de cola de mensajes de Windows. No es muy elegante, pero funciona.

+0

¿La cola de mensajes de Windows realmente garantiza su esquema de manejo de concurrencia? Apuesto a que no está bloqueado. – thiton

+0

Es mi, o no puede ser, sin cerradura. Más importante aún, es lento comparado con cualquier cola P-C de espacio de usuario 'normal' (por ejemplo, una cola con un CS que lo protege y un semáforo para bloquear). –

+0

OTOH, hay algo peor que un WMQ: ¡una cola de IOCP es aún más lenta! Al menos una WMQ es una solución mejor que una cola con plantilla, (leer como 'copia de datos excesiva y evitable'), libre de bloqueo por hilo de productor que todos tienen que ser sondeados por el consumidor. –

2

Ninguna biblioteca hará esto hasta donde yo sé, es demasiado compleja. Tendrás que hacer la tuya, y esta es una idea que acabo de tener, crear un archivo de registro por hilo, asegurarte de que el primer elemento de cada entrada sea una marca de tiempo y luego fusionar los registros después de ejecutar y ordenar (por marca de tiempo)) para obtener un archivo de registro final.

Puede usar algunos hilos de almacenamiento local (por ejemplo, un FILE manejar AFAIK no será posible almacenar un objeto de flujo en el almacenamiento local de subprocesos) y buscar este manejador en cada línea de registro y escribir en ese archivo específico .

¿Toda esta complejidad frente a bloquear el mutex? No conozco los requisitos de rendimiento de su aplicación, pero si es sensible, ¿por qué estaría registrando (excesivamente)? ¿Piensa en otras formas de obtener la información que necesita sin iniciar sesión?

También otra cosa a considerar es utilizar el mutex por el menor tiempo posible, es decir, construir primero su entrada de registro y luego, justo antes de escribir en el archivo, adquirir el bloqueo.

5

Recomendaría evitar el problema utilizando solo un hilo para el registro. Para pasar los datos necesarios para el registro, puede usar fifo queue sin bloqueo (hilo seguro siempre que el productor y el consumidor estén estrictamente separados y solo un hilo tenga cada rol). Por lo tanto, necesitará una cola para cada productor).

Ejemplo de cola libre de bloqueo rápido se incluye:

queue.h:

#ifndef QUEUE_H 
#define QUEUE_H 

template<typename T> class Queue 
{ 
public: 
    virtual void Enqueue(const T &element) = 0; 
    virtual T Dequeue() = 0; 
    virtual bool Empty() = 0; 
}; 

hybridqueue.h:

#ifndef HYBRIDQUEUE_H 
#define HYBRIDQUEUE_H 

#include "queue.h" 


template <typename T, int size> class HybridQueue : public Queue<T> 
{ 

public: 
    virtual bool Empty(); 
    virtual T Dequeue(); 
    virtual void Enqueue(const T& element); 
    HybridQueue(); 
    virtual ~HybridQueue(); 

private: 
    struct ItemList 
    { 
     int start; 
     T list[size]; 
     int end; 
     ItemList volatile * volatile next; 
    }; 

    ItemList volatile * volatile start; 
    char filler[256]; 
    ItemList volatile * volatile end; 
}; 

/** 
* Implementation 
* 
*/ 

#include <stdio.h> 

template <typename T, int size> bool HybridQueue<T, size>::Empty() 
{ 
    return (this->start == this->end) && (this->start->start == this->start->end); 
} 

template <typename T, int size> T HybridQueue<T, size>::Dequeue() 
{ 
    if(this->Empty()) 
    { 
     return NULL; 
    } 
    if(this->start->start >= size) 
    { 
     ItemList volatile * volatile old; 
     old = this->start; 
     this->start = this->start->next; 
      delete old; 
    } 
    T tmp; 
    tmp = this->start->list[this->start->start]; 
    this->start->start++; 
    return tmp; 
} 

template <typename T, int size> void HybridQueue<T, size>::Enqueue(const T& element) 
{ 
    if(this->end->end >= size) { 
     this->end->next = new ItemList(); 
     this->end->next->start = 0; 
     this->end->next->list[0] = element; 
     this->end->next->end = 1; 
     this->end = this->end->next; 
    } 
    else 
    { 
     this->end->list[this->end->end] = element; 
     this->end->end++; 
    } 
} 

template <typename T, int size> HybridQueue<T, size>::HybridQueue() 
{ 
    this->start = this->end = new ItemList(); 
    this->start->start = this->start->end = 0; 
} 

template <typename T, int size> HybridQueue<T, size>::~HybridQueue() 
{ 

} 

#endif // HYBRIDQUEUE_H 
+0

+1: una cola libre de bloqueo y un hilo de lectura es de hecho el camino a seguir –

+2

¿Qué? Una cola para cada productor: ¿cómo se va a gestionar? ¿Cómo funciona esta fila de todos modos? ¿Qué espera el productor cuando la cola está vacía? Un hilo de registrador, una cola de PC y un bloqueo solo alrededor de la cola, (es decir, la cola solo está bloqueada por el tiempo necesario para insertar una instancia de objeto, es decir, el valor de referencia de 32/64 bits), me parece más limpia . –

+0

Eso! Puede tener un vector o mapa de esas colas, donde puede asignar colas a los hilos, el consumidor simplemente itera sobre toda la serie de colas. El productor no espera, el productor simplemente encola más datos: P El consumidor puede ponerse para dormir, hacer otro trabajo, o OP puede introducir algún mecanismo de señalización en el impulso, la clase puede modificarse fácilmente. En cuanto al bloqueo, OP quería evitar eso. – Erbureth

5

Si consigo su pregunta Righ t le preocupa realizar operaciones de E/S (probablemente escribir en un archivo) en la sección crítica de un registrador.

Boost: log le permite definir un objeto de escritor personalizado. Puede definir operator() para llamar a la E/S asíncrona o pasar un mensaje a su hilo de registro (que está haciendo I/Os).

http://www.torjo.com/log2/doc/html/workflow.html#workflow_2b

6

Creo que su declaración es incorrecta: el uso de exclusión mutua no es necesario que equivale a una solución sincrónica. Sí, Mutex es para control de sincronización, pero se puede usar para muchas cosas diferentes.Podemos usar mutex en, por ejemplo, una cola de consumidor de productor mientras el registro todavía está sucediendo de forma asíncrona.

Honestamente, no he investigado la implementación de esta biblioteca de registro pero debería ser posible hacer un appendidor asíncrono (para log4j como lib) qué registrador escribe en una cola de consumidor productor y otro hilo trabajador es responsable de escribir en un archivo (o incluso delegar a otro appender), en caso de que no se proporcione.


Editar: acaba de tener una breve exploración en log4cxx, proporciona un AsyncAppender el que hace lo que yo sugerí: amortigua el registro de eventos entrantes, y delegar en appender unida de forma asíncrona.

+1

+1 para resaltar la gran diferencia de rendimiento entre bloquear una cola por el poco tiempo que se tarda en presionar un puntero/referencia y bloquear una operación de escritura de disco completa. –

1

Los algoritmos de bloqueo no son necesariamente los más rápidos. Defina sus límites ¿Cuántos hilos hay para iniciar sesión? ¿Cuánto se escribirá en una sola operación de registro como máximo?

Las operaciones de E/S vinculadas son mucho más lentas que el cambio de contexto de subprocesos debido a los hilos de bloqueo/activación. El uso de algoritmo de bloqueo sin bloqueo/giro con 10 hilos de escritura traerá una gran carga a la CPU.

En breve, bloquee otros hilos cuando está escribiendo en un archivo.

+0

Bloquee brevemente otros subprocesos cuando está presionando entradas de registro en una cola, incluso mejor. –

Cuestiones relacionadas