C# C语言中的线程安全属性#
我正在尝试在C#中创建线程安全属性,我希望确保我在正确的路径上-以下是我所做的-C# C语言中的线程安全属性#,c#,.net,multithreading,properties,thread-safety,C#,.net,Multithreading,Properties,Thread Safety,我正在尝试在C#中创建线程安全属性,我希望确保我在正确的路径上-以下是我所做的- private readonly object AvgBuyPriceLocker = new object(); private double _AvgBuyPrice; private double AvgBuyPrice { get { lock (AvgBuyPriceLocker) { return _AvgBuyPrice;
private readonly object AvgBuyPriceLocker = new object();
private double _AvgBuyPrice;
private double AvgBuyPrice
{
get
{
lock (AvgBuyPriceLocker)
{
return _AvgBuyPrice;
}
}
set
{
lock (AvgBuyPriceLocker)
{
_AvgBuyPrice = value;
}
}
}
读了这篇文章,似乎这不是正确的做法-
但这篇文章似乎提出了相反的建议,
有人有更明确的答案吗
编辑:
我想为这个属性执行Getter/Setter的原因是b/c,我实际上希望它在设置时触发一个事件,所以代码实际上是这样的-
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);
}
}
}
}
我对使我的代码线程安全还很陌生,所以如果我用了完全错误的方法,请随时告诉我
Will由于您有一个原语值,因此此锁定将正常工作-另一个问题中的问题是属性值是一个更复杂的类(可变引用类型)-锁定将保护访问和检索您的类所持有的双精度值的实例 另一方面,如果您的属性值是可变引用类型,则一旦使用其方法检索到类实例,锁定将无法防止更改,而这正是其他海报希望它执行的操作。无论如何,读取和写入double都是原子的()读取和写入double不是原子的,因此有必要使用锁保护对double的访问,但是对于许多类型,读取和写入是原子的,因此以下内容同样安全:
private float AvgBuyPrice
{
get;
set;
}
我的观点是线程安全比简单地保护每个属性更复杂。举一个简单的例子,假设我有两个属性AvgBuyPrice
和StringAvgBuyPrice
:
private string StringAvgBuyPrice { get; set; }
private float AvgBuyPrice { get; set; }
假设我更新了平均购买价格:
this.AvgBuyPrice = value;
this.StringAvgBuyPrice = value.ToString();
这显然不是线程安全的,以上述方式单独保护属性毫无帮助。在这种情况下,锁定应该在不同的级别上执行,而不是在每个属性级别上执行。您编写的锁是没有意义的。例如,读取变量的线程将:
话虽如此,在某些情况下,访问变量时需要锁定。这通常是由于底层处理器的原因造成的:例如,在32位机器上,
double
变量不能作为单个指令读取或写入,因此必须锁定(或使用替代策略)以确保不读取损坏的值。线程安全性不应添加到变量中,这是你应该添加到你的“逻辑”中的东西。如果向所有变量添加锁,代码仍然不一定是线程安全的,但速度会非常慢。
要编写线程安全程序,请查看您的代码并确定多个线程可以在何处使用相同的数据/对象。在所有关键位置加锁或其他安全措施
例如,假设以下一位伪代码:
void updateAvgBuyPrice()
{
float oldPrice = AvgBuyPrice;
float newPrice = oldPrice + <Some other logic here>
//Some more new price calculation here
AvgBuyPrice = newPrice;
}
void updateAvgBuyPrice()
{
浮动oldPrice=AvgBuyPrice;
浮动新价格=旧价格+
//这里有一些新的价格计算
AvgBuyPrice=新价格;
}
如果同时从多个线程调用此代码,则锁定逻辑没有用处。想象一个线程获取AvgBuyPrice并进行一些计算。现在,在完成之前,线程B还将获取AvgBuyPrice并开始计算。线程A同时完成,并将新值分配给AvgBuyPrice。然而,就在片刻之后,它将被线程B(仍然使用旧值)覆盖,线程A的工作已经完全丢失
那么你如何解决这个问题呢?如果我们要使用锁(这将是最丑陋和最慢的解决方案,但如果您刚开始使用多线程,这将是最简单的解决方案),我们需要将所有改变AvgBuyPrice的逻辑放在锁中:
void updateAvgBuyPrice()
{
lock(AvgBuyPriceLocker)
{
float oldPrice = AvgBuyPrice;
float newPrice = oldPrice + <Some other code here>
//Some more new price calculation here
AvgBuyPrice = newPrice;
}
}
void updateAvgBuyPrice()
{
锁(AvgBuyPriceLocker)
{
浮动oldPrice=AvgBuyPrice;
浮动新价格=旧价格+
//这里有一些新的价格计算
AvgBuyPrice=新价格;
}
}
现在,如果线程B想在线程A仍然忙的时候进行计算,它将等待线程A完成,然后使用新值进行工作。但是请记住,任何其他也修改AvgBuyPrice的代码在工作时也应该锁定AvgBuyPriceLocker
不过,如果经常使用,速度会很慢。锁很昂贵,还有很多其他机制可以避免锁,只需搜索无锁算法。虽然这是一个老问题,但它在谷歌搜索中占据了上风,所以我添加了一个回答。在您的示例中,
get
locker在return
之后不会被释放。因此,在这种情况下,我建议在try finally
块中使用readerwriterlocksim
,这非常适合您尝试实现的结果。它允许多线程读取或独占访问写入:
private readonly ReaderWriterLockSlim AvgBuyPriceLocker = new ReaderWriterLockSlim();
private double _AvgBuyPrice = 0;
public double AvgBuyPrice {
get {
AvgBuyPriceLocker.EnterReadLock();
try { return _AvgBuyPrice; }
finally { AvgBuyPriceLocker.ExitReadLock(); }
}
set {
AvgBuyPriceLocker.EnterWriteLock();
try { _AvgBuyPrice = value; }
finally { AvgBuyPriceLocker.ExitWriteLock(); }
}
}
没关系。我不明白,为什么你认为不是。链接的SO答案并不表示这样做是个坏主意。var property=new ConcurrentValue();property.ReadValue(x=>