2011-09-26 14 views
8

En algunos lugares, he visto personas creando un grupo de subprocesos y creando subprocesos y ejecutando una función con esos subprocesos. Al llamar a esa función boost :: mutex se pasa por referencia. ¿Por qué se hace así? Creo que puede tener un mutex declarado en la función llamada en sí o puede declararse miembro de clase o global. ¿Alguien puede explicar?¿Por qué pasar mutex como parámetro a una función a la que llama un hilo?

p. Ej.

myclass::processData() 
    { 
     boost::threadpool::pool pool(2); 
     boost::mutex mutex; 

     for (int i =0; data<maxData; ++data) 
      pool.schedule(boost::bind(&myClass::getData, boost_cref(*this), boost::ref(mutex))); 
    } 

Entonces,

myClass::getData(boost::mutex& mutex) 
    { 
     boost::scoped_lock(mutex) // Why can't we have class member variable mutex or          
             //local mutex here 
     //Do somethign Here 
} 
+2

Debe agregar un ejemplo particular donde se usa. Probablemente se deba a la semántica que se implementa en el ejemplo, y no se puede responder sin contexto (a menos que desee genérico * solo porque * o * pueda tener sentido * respuestas) –

Respuesta

10

mutex de objetos no son copiables, y si bien pueden ser miembros de una clase, que complicaría enormemente la capacidad de copiar una clase padre. Por lo tanto, un método preferido, si un número de instancias de clase necesita compartir los mismos datos, sería crear el mutex como un miembro de datos estático. De lo contrario, si el mutex solo necesita estar bloqueado dentro de una instancia de la clase, puede crear un puntero a un mutex como un miembro de datos no estático, y luego cada copia de la clase tendrá su propio mutex asignado dinámicamente (y seguir siendo copiables si eso es un requisito).

En el ejemplo de código anterior, lo que básicamente ocurre es que hay un mutex global que se pasa al grupo de subprocesos por referencia. Esto permite que todos los subprocesos que comparten las mismas ubicaciones de memoria creen un bloqueo exclusivo en esa memoria usando el mismo mutex exacto, pero sin la sobrecarga de tener que administrar el aspecto no copiable del mutex mismo. El mutex en este ejemplo de código también podría haber sido un miembro de datos estático de la clase myClass en lugar de un mutex global que se pasa por referencia, suponiendo que cada hilo debería bloquear alguna memoria a la que se pueda acceder globalmente desde cada hilo.

El problema con un mutex local es que es solo una versión localmente accesible del mutex ... por lo tanto, cuando un hilo bloquea el mutex para compartir algunos datos accesibles globalmente, los datos no están protegidos, ya que thread tendrá su propio mutex local que se puede bloquear y desbloquear. Derrota todo el punto de exclusión mutua.

+1

Esto no tiene sentido, hay muchas aplicaciones donde tener mutexes como miembros no estáticos tiene sentido. Mucho más a menudo que tener el mutex como un miembro estático, y no hay razón para no almacenar un puntero o referencia como miembro. El mutex debe estar al nivel de los datos que se protegen (es decir, si protege los datos estáticos, debe ser estático, si protege los atributos de los miembros, entonces probablemente debería ser un miembro, si es compartido por diferentes instancias o diferente tipos, entonces debe ser aún más general –

+0

Lo siento, no quise decir que * no puedo * hacerlo ... solo estaba diciendo que si lo convierte en un miembro de datos no estático, creando un archivo correctamente La clase de copiado es mucho más difícil (al final no se puede copiar, tendría que guardar un puntero en un mutex y luego desasignar y reasignar el mutex en la copia, que no es una copia "verdadera" de cada miembro de la clase). Revisaré mi respuesta para aclarar esto. – Jason

+2

... todo depende de la definición de su objeto (semánticamente) En la mayoría de los casos, el mutex no forma parte del estado del objeto, pero es una utilidad para garantizar que el estado real de la clase no pueda verse en un intermediario iate un estado incorrecto (invariantes roto) cuando se usa en un entorno multiproceso. –

0

El uso de mutex local es incorrecto: el grupo de subprocesos puede invocar varias instancias de función, y deben funcionar con el mismo mutex. El miembro de la clase está bien. Pasar mutex a la función lo hace más genérico y legible. La persona que llama puede decidir qué mutex pasar: miembro de la clase o cualquier otra cosa.

+0

Esto me frota mal. Si corresponde a la clase llamante cómo usar el Mutex, ¿no debería formar parte de la definición de la interfaz? – kenny

1

Creo que puede tener un mutex declarado en la función llamada en sí o puede declararse como un miembro de clase o global. ¿Alguien puede explicar?

creando un nuevo mutex en la entrada no protege nada.

si estaba pensando en declarar un mutex estático (o global) para proteger a los miembros no estáticos, entonces también puede escribir el programa como un único programa de subprocesos (vale, hay algunos casos de esquina). un bloqueo estático bloquearía todos los hilos excepto uno (asumiendo el concurso); es equivalente a "un máximo de un hilo puede operar en el cuerpo de este método a la vez". declarar un mutex estático para proteger los datos estáticos está bien. como David Rodríguez - Dribeas lo formuló sucintamente en los comentarios de otra respuesta: "El mutex debería estar al nivel de los datos que se están protegiendo".

que puede declara una variable miembro, por ejemplo, que tomaría la forma generalizada:

class t_object { 
public: 
    ... 
    bool getData(t_data& outData) { 
     t_lock_scope lock(this->d_lock); 
     ... 
     outData.set(someValue); 
     return true; 
    } 

private: 
    t_lock d_lock; 
}; 

que el enfoque está muy bien, y en algunos casos ideales. tiene sentido en la mayoría de los casos cuando construyes un sistema donde las instancias intentan abstraer la mecánica de bloqueo y los errores de sus clientes. Una desventaja es que puede requerir más adquisiciones y típicamente requiere mecanismos de bloqueo más complejos (por ejemplo, reentrada). por más adquisiciones: el cliente puede saber que una instancia se utiliza en un solo hilo: ¿por qué bloquear en ese caso? Además, un montón de pequeños métodos de fabricación de hilos introducirán una gran cantidad de sobrecarga. con el bloqueo, quiere entrar y salir de las zonas protegidas lo antes posible (sin introducir muchas adquisiciones), por lo que las secciones críticas son a menudo operaciones más grandes que las típicas.

si la interfaz pública requiere este bloqueo como un argumento (como se ve en el ejemplo), es una señal de que su diseño se puede simplificar mediante la privatización de bloqueo (haciendo la función de objeto de una manera segura hilo, en lugar de pasar el bloqueo como un recurso externo).

utilizando un bloqueo externo (o vinculado o asociado), puede reducir las adquisiciones (o el tiempo total bloqueado). este enfoque también le permite agregar bloqueo a una instancia después del hecho. también le permite al cliente configurar cómo funciona el bloqueo. el cliente puede usar menos bloqueos al compartirlos (en un conjunto de instancias). incluso un simple ejemplo de composición puede ilustrar este (con soporte para ambos modelos):

class t_composition { 
public: 
    ... 
private: 
    t_lock d_lock; // << name and data can share this lock 
    t_string d_name; 
    t_data d_data; 
}; 

teniendo en cuenta la complejidad de algunos sistemas multiproceso, empujando la responsabilidad de bloqueo apropiada sobre el cliente puede ser una muy mala idea .

ambos modelos (vinculados y como variable de miembro) se pueden utilizar con eficacia. lo que es mejor en un escenario dado varía según el problema.

+0

Gracias Justin. Pero, ¿significa que si desea proteger los datos estáticos, entonces necesita un mutex estático y si desea proteger el miembro de datos, necesita mutex de nivel de clase como regla general? – polapts

+0

como un enfoque * general *: sí, ese es uno de los múltiples enfoques utilizables para garantizar que sus implementaciones sean seguras para subprocesos (otro es el enfoque en el OP). la granularidad también es importante si desea minimizar las adquisiciones y los tiempos de bloqueo. un bloqueo de nivel de clase puede ser demasiado pequeño en algunos casos, esto dará lugar a muchas adquisiciones innecesarias. un bloqueo más grande (global o basado en documentos) no será ideal en muchos casos porque el tiempo bloqueado por adquisición aumenta y hay una probabilidad mucho mayor de que otros esperen durante ese tiempo. – justin

Cuestiones relacionadas