2009-08-17 28 views
16

Estoy tratando de bloquear un objeto "en caja" en una aplicación C#, ¿no es esto posible?C# threading - Bloquear Objeto

class t 
    { 
     System.Object t_x = new object(); 

     public t(int p) 
     { 
      t_x = p; 

     } 

     public void w() 
     { 
      lock (t_x) 
      { 
       for (int i = 0; i < 4; i++) 
       { 

        { 
         t_x = ((int)t_x) + 1; 
         Console.WriteLine(t_x); 
         Thread.Sleep(1000); 
        } 
       } 
      } 
     } 
    } 

En otra clase que pueda empezar a 2 hilos:

 Thread b1 = new Thread(new ThreadStart(t1.w)); 
     b1.Start(); 
     Thread b2 = new Thread(new ThreadStart(t1.w)); 
     b2.Start(); 

Sin embargo, la parte que no está bloqueado. Cuando bloqueo un objeto arbitrario (es decir, uno creado y no modificado como objeto a = new object()) se bloquea bien. ¿La operación de boxeo de alguna manera "deprociona" mi Objeto?

+2

¿Por qué volver a asignar el objeto de bloqueo dentro de un bloque de bloqueo? Eso frustra el propósito, ya que el objetivo de la sincronización es evitar que los subprocesos se pisoteen entre sí para obtener un recurso "compartido". – Gishu

+0

Creo que la respuesta acepto no es la buena –

+0

Parece haber un error recurrente entre los que intentan con menos frecuencia .Net threading que la instrucción de bloqueo en realidad "bloquea" o protege el objeto al que se llama el bloqueo. Parecen pasar por alto el hecho de que el objeto es realmente la clave de un bloque bloqueado de código, la sección crítica. –

Respuesta

16

Necesita crear un objeto de bloqueo por separado. El problema es que reasignas t_x dentro del ciclo. Suponiendo que el subproceso b1 entre en el ciclo antes de que b2 llegue a la instrucción de bloqueo, se permitirá b2 dentro de la instrucción de bloqueo porque, en ese momento, t_x será un objeto nuevo que no tiene un bloqueo.

+0

Creo que Monitorear es una mejor manera de hacerlo –

+4

@Cedric - lea la respuesta de Lee con más cuidado. La instrucción 'lock' es exactamente lo mismo que usar' Monitor.Enter' y 'Monitor.Exit'. Es solo una sintaxis más ordenada para el mismo patrón. El problema no es si usar 'lock' o' Monitor' (no es realmente una opción, ya que son lo mismo). El problema es pasar siempre el mismo objeto para que funcione como el bloqueo, que se rompe si usa un tipo de valor, ya que está encuadrado de manera diferente cada vez que accede a él. –

23

No, no se puede hacer esto - el bloque de bloqueo es la abreviatura de lo siguiente:

try(Monitor.Enter(lockObject)) 
{ 
    //critical section 
} 
finally 
{ 
    Monitor.Exit(lockObject) 
} 

Los documentation for Monitor.Enter estados, "Uso del monitor para bloquear los objetos (es decir, los tipos de referencia), y no los tipos de valor Cuando pasa una variable de tipo de valor a Entrar, se coloca como un objeto. Si pasa la misma variable a Ingresar de nuevo, se coloca en un cuadro como un objeto separado y el hilo no bloquea "

+0

Esa es la buena manera de hacerlo. Está en el primer examen 70-536 de la certificación de Microsoft. –

+7

Esta es la respuesta correcta, no debido al fragmento de código (la instrucción 'lock' está bien) sino porque menciona la diferencia entre el valor y los tipos de referencia. Un tipo de valor se vuelve a empaquetar cada vez que se convierte a la clase base universal 'object', por lo que no puede actuar de manera fiable como objetivo de bloqueo. Entonces esto es mejor que la respuesta aceptada. –

2

Tiene que use un objeto adicional para la cerradura

object lockObj = new object(); 
public void foo() 
{ 
    lock(lockObj) 
    { 
    //do stuff here 
    } 
} 
2

La llamada de bloqueo (t_x) coloca un número entero como un objeto temporal. Cada llamada para bloquear (t_x) crea un objeto nuevo y el bloqueo es inútil.

(Bloqueo espera un objeto y crea un objeto temporal NUEVA del número entero)

Basta con crear un objeto de bloqueo separado, como se dijo anteriormente por Femaref.

0

Si realmente quiere (? Necesita) para bloquear en el objeto, se puede utilizar un tipo de envoltorio:

public class IntWrapper 
{ 
    public int Value{get;set;} 
} 

O si lo necesita para mantenerse más abstracto:

public class ObjectWrapper 
{ 
    public Object Value { get;set; } 
} 
+0

Si bien no recomendaría hacerlo de esta manera (solo use otro 'objeto' para bloquear), no entiendo los votos abajo, ya que * es * correcto. –

0

Si desea reconocer cuándo se cargan los datos y también si el uso intenta usarlos antes, puede hacer algo como esto:

Tener un indicador booleano como usted menciona, pero use un objeto separado para bloquearlo antes acceder a él para evitar condiciones de carrera cruzada.

Cuando el usuario intenta usar los datos, si no está cargado (verifique la variable) puede agregar otro controlador de eventos al evento RunWorkerCompleted del trabajador, que hará inmediatamente lo que el usuario desee cuando se carguen los datos.

Ejemplo:

public class MyClass 
{ 
    private bool dataIsReady = false; 
    private object locker = new object(); 
    BackgroundWorker worker; 

    public void Begin() 
    { 
     worker = new BackgroundWorker(); 
     worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
    } 

    public void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     lock (locker) 
     { 
      dataIsReady = true; 
     } 
    } 

    public void UseTriesToUseData() 
    { 
     lock (locker) 
     { 
      if (dataIsReady) 
      { 
       DoStuff(); 
      } 
      else 
      { 
       this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DoStuffCaller); 
      } 
     } 
    } 

    private void DoStuff() 
    { 
     // Do stuff with data. 
    } 

    private void DoStuffCaller(object sender, RunWorkerCompletedEventArgs e) 
    { 
     this.DoStuff(); 
    } 
}