thread - Propiedades seguras del hilo en C#
thread c# ejemplo (4)
Como tiene un valor primitivo, este bloqueo funcionará bien. El problema en la otra pregunta era que el valor de la propiedad era una clase más compleja (un tipo de referencia mutable). El bloqueo protegerá el acceso y la recuperación de la instancia del valor doble que posee. Tu clase.
Si el valor de su propiedad es un tipo de referencia mutable, por otra parte, el bloqueo no protegerá de cambiar la instancia de la clase una vez recuperada utilizando sus métodos, que es lo que el otro póster quería que hiciera.
Estoy tratando de crear propiedades seguras para subprocesos en C # y quiero asegurarme de que estoy en el camino correcto. Esto es lo que he hecho.
private readonly object AvgBuyPriceLocker = new object();
private double _AvgBuyPrice;
private double AvgBuyPrice
{
get
{
lock (AvgBuyPriceLocker)
{
return _AvgBuyPrice;
}
}
set
{
lock (AvgBuyPriceLocker)
{
_AvgBuyPrice = value;
}
}
}
Al leer esta publicación, parece que esta no es la forma correcta de hacerlo.
C # hilo de seguridad con get / set
Sin embargo, este artículo parece sugerir lo contrario.
http://www.codeproject.com/KB/cs/Synchronized.aspx
¿Alguien tiene una respuesta más definitiva?
Editar:
La razón por la que quiero hacer el Getter / Setter para esta propiedad es b / c. De hecho, quiero que se dispare un evento cuando está configurado, por lo que el código sería así:
public class PLTracker
{
public PLEvents Events;
private readonly object AvgBuyPriceLocker = new object();
private double _AvgBuyPrice;
private double AvgBuyPrice
{
get
{
lock (AvgBuyPriceLocker)
{
return _AvgBuyPrice;
}
}
set
{
lock (AvgBuyPriceLocker)
{
Events.AvgBuyPriceUpdate(value);
_AvgBuyPrice = value;
}
}
}
}
public class PLEvents
{
public delegate void PLUpdateHandler(double Update);
public event PLUpdateHandler AvgBuyPriceUpdateListener;
public void AvgBuyPriceUpdate(double AvgBuyPrice)
{
lock (this)
{
try
{
if (AvgBuyPriceUpdateListener!= null)
{
AvgBuyPriceUpdateListener(AvgBuyPrice);
}
else
{
throw new Exception("AvgBuyPriceUpdateListener is null");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
Soy bastante nuevo en hacer que mi hilo de código sea seguro, así que no dude en decirme si lo estoy haciendo de la manera más equivocada.
Será
La seguridad del hilo no es algo que deba agregar a sus variables, es algo que debe agregar a su "lógica". Si agrega bloqueos a todas sus variables, su código no será necesariamente seguro para subprocesos, pero será lento como el infierno. Para escribir un programa seguro para subprocesos, mire su código y decida dónde múltiples subprocesos podrían estar usando los mismos datos / objetos. Agregue candados u otras medidas de seguridad a todos esos lugares críticos.
Por ejemplo, asumiendo el siguiente bit de pseudo código:
void updateAvgBuyPrice()
{
float oldPrice = AvgBuyPrice;
float newPrice = oldPrice + <Some other logic here>
//Some more new price calculation here
AvgBuyPrice = newPrice;
}
Si este código se llama desde varios subprocesos al mismo tiempo, su lógica de bloqueo no tiene uso. Imagina el hilo A obteniendo AvgBuyPrice y haciendo algunos cálculos. Ahora, antes de que se haga, el subproceso B también obtiene el AvgBuyPrice y comienza los cálculos. El subproceso A mientras tanto se realiza y asignará el nuevo valor a AvgBuyPrice. Sin embargo, solo unos momentos más tarde, se sobrescribirá con el subproceso B (que aún utiliza el valor anterior) y el trabajo del subproceso A se ha perdido por completo.
Entonces, ¿cómo se puede arreglar esto? Si tuviéramos que usar bloqueos (lo que sería la solución más fea y lenta, pero la más fácil si solo está empezando con multiproceso), debemos poner toda la lógica que cambia AvgBuyPrice en bloqueos:
void updateAvgBuyPrice()
{
lock(AvgBuyPriceLocker)
{
float oldPrice = AvgBuyPrice;
float newPrice = oldPrice + <Some other code here>
//Some more new price calculation here
AvgBuyPrice = newPrice;
}
}
Ahora, si el subproceso B quiere hacer los cálculos mientras el subproceso A todavía está ocupado, esperará hasta que el subproceso A termine y luego hará su trabajo con el nuevo valor. Sin embargo, tenga en cuenta que cualquier otro código que también modifique AvgBuyPrice también debe bloquear AvgBuyPriceLocker mientras esté funcionando.
Aún así, esto será lento si se usa a menudo. Los bloqueos son caros y hay muchos otros mecanismos para evitar los bloqueos, simplemente busque algoritmos sin bloqueo.
Las cerraduras, como las has escrito, no tienen sentido. El hilo que lee la variable, por ejemplo, será:
- Adquirir el bloqueo.
- Lea el valor.
- Suelte la cerradura.
- Utilice el valor de lectura de alguna manera.
No hay nada que impida que otro hilo modifique el valor después del paso 3. Como el acceso variable en .NET es atómico (vea la advertencia a continuación), el bloqueo no está logrando mucho en este punto: simplemente agrega una sobrecarga. Contraste con el ejemplo desbloqueado:
- Lea el valor.
- Utilice el valor de lectura de alguna manera.
Otro hilo puede alterar el valor entre los pasos 1 y 2 y esto no es diferente del ejemplo bloqueado.
Si desea asegurarse de que el estado no cambie cuando está realizando algún procesamiento, debe leer el valor y realizar el procesamiento utilizando ese valor dentro del contexto del bloqueo:
- Adquirir el bloqueo.
- Lea el valor.
- Utilice el valor de lectura de alguna manera.
- Suelte la cerradura.
Dicho esto, hay casos en los que es necesario bloquear cuando se accede a una variable. Por lo general, esto se debe a razones con el procesador subyacente: una variable double
no puede leerse ni escribirse como una sola instrucción en una máquina de 32 bits, por ejemplo, por lo que debe bloquear (o usar una estrategia alternativa) para asegurarse de que un valor corrupto no esté leer.
La lectura y escritura de dobles es atómica de todos modos ( source ) la lectura y escritura de dobles no es atómica, por lo que sería necesario proteger el acceso a un doble utilizando un candado, sin embargo, para muchos tipos, la lectura y escritura es atómica, por lo que lo siguiente ser igual de seguro:
private float AvgBuyPrice
{
get;
set;
}
Mi punto es que la seguridad de los hilos es más compleja que simplemente proteger cada una de sus propiedades. Para dar un ejemplo simple, supongamos que tengo dos propiedades AvgBuyPrice
y StringAvgBuyPrice
:
private string StringAvgBuyPrice { get; set; }
private float AvgBuyPrice { get; set; }
Y supongamos que actualizo el precio medio de compra así:
this.AvgBuyPrice = value;
this.StringAvgBuyPrice = value.ToString();
Esto claramente no es seguro para subprocesos y las propiedades de protección individual de la manera anterior no ayudarán en absoluto. En este caso, el bloqueo se debe realizar en un nivel diferente en lugar de en un nivel por propiedad.