Tarea de bloqueo múltiple (threading)

Necesito implementar la clase que debe realizar el mecanismo de bloqueo en nuestro marco. Tenemos varios hilos y están numerados 0,1,2,3.... Tenemos una clase estática llamada ResourceHandler, que debería bloquear estos hilos en objetos dados. El requisito es que n Lock() las invocaciones deben ser liberadas por m Release() invoca, donde n = [0..] ym = [0..]. Así que no importa cuántos bloqueos se realizaron en un solo objeto, solo uno Release() la llamada es suficiente para desbloquear todo. Aún más si el objeto no está bloqueado, Release() la llamada no debe realizar nada. También necesitamos saber qué objetos están bloqueados en qué subprocesos.

Tengo esta implementación:

public class ResourceHandler
{
    private readonly Dictionary<int, List<object>> _locks = new Dictionary<int, List<object>>();

    public static ResourceHandler Instance {/* Singleton */}

    public virtual void Lock(int threadNumber, object obj)
    {
        Monitor.Enter(obj);

        if (!_locks.ContainsKey(threadNumber)) {_locks.Add(new List<object>());}
        _locks[threadNumber].Add(obj);
    }

    public virtual void Release(int threadNumber, object obj)
    {
       // Check whether we have threadN in _lock and skip if not
       var count = _locks[threadNumber].Count(x => x == obj);
       _locks[threadNumber].RemoveAll(x => x == obj);

       for (int i=0; i<count; i++)
       {
           Monitor.Exit(obj);
       }
    }

    // .....
 }

En realidad, lo que me preocupa aquí es la seguridad de los subprocesos. De hecho, no estoy seguro de si es seguro para subprocesos o no, y es un verdadero dolor solucionarlo. ¿Estoy haciendo la tarea correctamente y cómo puedo asegurarme de que esto sea seguro para subprocesos?

preguntado el 04 de julio de 12 a las 09:07

No estoy seguro de entender: se supone que un subproceso debe detener a otro o ¿el subproceso es el número del subproceso actual? -

threadN es el número del hilo actual. Lock and Release debería bloquear y liberar hilos como Monitor.Enter/Exit está haciendo eso, pero la regla sobre muchos bloqueos: muchas liberaciones deben cumplirse. También necesito contar los objetos que están bloqueados y conocer sus números de subprocesos (tenemos algo de limpieza para cada subproceso, donde podemos liberar todos los objetos en el bloque final para asegurarnos de que todos los bloqueos se liberen cuando la aplicación se detiene) -

He cambiado threadN a threadNumber -

¿Por qué estás escribiendo tu propia clase de bloqueo? ¿Hay algún problema con las integradas? -

@Jodrell El construido no admite el requisito "una liberación" libera "muchos bloqueos" y la liberación del objeto no bloqueado no hace nada. Es un requisito del cliente, y aunque no estoy seguro de por qué necesitamos eso, necesito hacerlo. Y también necesito contar objetos bloqueados con sus números de hilo:

4 Respuestas

Tus Lock el método se bloquea en el objetivo objects pero el _locks Cualquier subproceso puede acceder al diccionario en cualquier momento. Es posible que desee agregar un objeto de bloqueo privado para acceder al diccionario (tanto en el Lock y Release métodos).

También tenga en cuenta que al usar un ResourceHandler de este tipo es responsabilidad del resto del código (los subprocesos de consumo) liberar todos los objetos usados ​​(un lock () block por ejemplo cubre ese problema ya que cada vez que dejas el lockalcance de , el objeto se libera).

También es posible que desee utilizar ReferenceEquals al contar el número de veces que un objeto está bloqueado en lugar de ==.

Respondido 04 Jul 12, 11:07

Poner un candado es un problema. Por ejemplo, no puedo colocar un lock() {} dentro del método Lock(), ya que Monitor.Enter() ya existe y recibiremos un interbloqueo. Y sobre la liberación, sí, esa es una de las razones por las que necesito contar todos los objetos bloqueados y sus números de subprocesos. Archeg

No debe incluir la Monitor llamadas dentro de la cerradura, solo el acceso al diccionario. Se producirán interbloqueos cuando los subprocesos se bloqueen en más de un objeto a la vez y no siempre en el mismo orden. - C.Evenhuis

En ese caso, tengo un problema con el código como en mi método Release. Tengo un código que cuenta bloqueos y devuelve la variable de conteo. Y necesito realizar tantos lanzamientos como esta variable de conteo. Puedo bloquear el uso del diccionario, pero todavía tengo esta variable de recuento insegura: Archeg

El count variable es una variable local, por lo que otros subprocesos no pueden hacer referencia a ella. Dado que el objeto para el que se cuenta todavía está bloqueado por el subproceso actual, ningún otro subproceso puede bloquearlo, por lo que ningún otro subproceso puede almacenar el valor en el diccionario durante el count círculo. - C.Evenhuis

Si comete un error, el subproceso A podría llamar Monitor.Exit para los objetos bloqueados por el subproceso B, lo que hace que el subproceso B ya no pueda liberar sus bloqueos. - C.Evenhuis

Puede asegurarse de que esta clase sea segura para subprocesos mediante el uso de un Diccionario concurrente pero no lo ayudará con todos los problemas que tendrá al tratar de desarrollar su propio mecanismo de bloqueo.

Hay varios mecanismos de bloqueo de números que ya forman parte de .Net Framework, debe usarlos.

Parece que necesitará usar una combinación de estos, incluidos manijas de espera para lograr lo que quieres.


EDITAR

Después de leer más detenidamente, creo que es posible que necesite un EventWaitHandle

Respondido 04 Jul 12, 10:07

¿Puede sugerir alguno que se ajuste a mis necesidades? - Archeg

Todavía no estoy seguro de cómo hacer eso con los eventos. Me pasé todo el día de ayer tratando de usarlos aquí. Parece bastante complicado - Archeg

Lo que tienes conceptualmente parece peligroso; esto es porque llama a Monitor.Enter y Monitor.Exit para que trabajen como Lock declaración, se recomienda que se encapsule en un try/finally bloque, es decir, para garantizar que se ejecuten secuencialmente. Vocación Monitor.Exit antes Monitor.Enter lanzará una excepción.

Para evitar estos problemas (si se lanza una excepción, el bloqueo para un subproceso determinado puede tomarse o no, y si se toma un bloqueo, no se liberará, lo que resultará en un bloqueo filtrado. Recomendaría usar uno de las opciones proporcionadas en las otras respuestas anteriores. Sin embargo, si desea avanzar con este mecanismo, CLR 4.0 agregó la siguiente sobrecarga al Monitor.Enter Método

public static void Enter (object, ref bool lockTaken);

lockTaken es falso si y solo si el Enter El método arroja una excepción y no se tomó el bloqueo. Entonces, usando sus dos métodos usando un global bool lockTaken puede crear algo como (aquí el ejemplo es para un solo casillero; necesitará un Diccionario de List<bool> correspondiente a sus hilos - o mejor dicho un Tuple). Así que en tu método Lock tendrías algo como

bool lockTaken = false;
Monitor.Enter(locker, ref lockTaken);

en el otro metodo Release

if (lockTaken)
    Monitor.Exit(locker);

Espero que esto ayude.

Editar: no creo que aprecie completamente su problema, pero por lo que puedo deducir, estaría usando un Colección concurrente. Estos son totalmente seguros para la cabeza. Verificar IProducerConsumerCollection<T> y ConcurrentBag<T>. Estos deberían facilitar lo que desea con todos los subprocesos a cargo del marco (nota: ¡una colección segura para subprocesos no significa que el código que ejecuta sea seguro para subprocesos!). Sin embargo, es probable que usar una colección como esta sea mucho más lento que usar bloqueos.

Respondido 04 Jul 12, 11:07

Puedo asegurarme de que todos los bloqueos se liberen enumerando mi diccionario y liberando los bloqueos al final del hilo (en realidad, todos los hilos finalmente se bloquean con eso). Esta sobrecarga se ve bastante bien, y estaba tratando de usarla, pero aún no estoy seguro de cómo lograr una seguridad total de subprocesos aquí. Necesito calcular cuántas veces se llamó al método Lock; eso significa que tengo un diccionario o una lista que no es seguro para subprocesos. E incluso si uso ConcurrentDictionary, todavía tengo un código no seguro para subprocesos, como enumerar el diccionario y llamar a lanzamientos: Archeg

He agregado una pequeña edición con otras colecciones concurrentes para mirar. Con lo anterior estoy diciendo que para cada locker incluir un bloqueo Tomado en el Dictonary/Tuple, luego agrega a la tupla el conteo Y si el bloqueo se toma o no. Esto asegurará que no esté liberando bloqueos que no hayan sido 'tomados'. Buena suerte... - Caballero de la Luna

En mi opinión, debe usar un conjunto atómico de funciones para que sea seguro.

http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx

Mutexes supongo que te ayudará.

Respondido 04 Jul 12, 10:07

Quiero saber todas las colinas y caídas de este problema. Estoy usando Monitor ahora, ¿no es una operación atómica? No necesito el bloqueo entre procesos, por lo que supongo que Monitor debería hacer lo mismo que Mutex en mi situación: Archeg

Si, tienes razón. El monitor es lo que necesitas. Para asegurarse de ejecutar una prueba exhaustiva durante el fin de semana con miles de subprocesos en el generador de perfiles. Para asegurarse de que implementó todo bien. Algunas pruebas de estrés. - boris ivanov

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.