C# 中的线程安全属性
我正在尝试在 C# 中创建线程安全属性,我想确保我处于正确的路径上 - 这是我所做的 -
private readonly object AvgBuyPriceLocker = new object();
private double _AvgBuyPrice;
private double AvgBuyPrice
{
get
{
lock (AvgBuyPriceLocker)
{
return _AvgBuyPrice;
}
}
set
{
lock (AvgBuyPriceLocker)
{
_AvgBuyPrice = value;
}
}
}
阅读这篇文章,这似乎不是正确的方法-
然而,本文似乎另有建议,
<一href="http://www.codeproject.com/KB/cs/Synchronized.aspx" rel="noreferrer">http://www.codeproject.com/KB/cs/Synchronized.aspx
有人吗有更确定的答案吗?
编辑:
我想为此属性执行 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);
}
}
}
}
我对编写代码还很陌生线程安全,所以请随时告诉我我是否以完全错误的方式进行操作!
将要
I am trying to create thread safe properties in C# and I want to make sure that I am on the correct path - here is what I have done -
private readonly object AvgBuyPriceLocker = new object();
private double _AvgBuyPrice;
private double AvgBuyPrice
{
get
{
lock (AvgBuyPriceLocker)
{
return _AvgBuyPrice;
}
}
set
{
lock (AvgBuyPriceLocker)
{
_AvgBuyPrice = value;
}
}
}
Reading this posting, it would seem as if this isn't the correct way of doing it -
however, this article seems to suggest otherwise,
http://www.codeproject.com/KB/cs/Synchronized.aspx
Does anybody have a more definitive answer?
Edit:
The reason that I want to do the Getter/Setter for this property is b/c I actually want it to fire an event when it is set - so the code would actually be like this -
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);
}
}
}
}
I am pretty new to making my code thread safe so please feel free to tell me if I am going about it in the totally wrong way!
Will
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
正如您所写的那样,锁是毫无意义的。例如,读取变量的线程将:
在步骤 3 之后,没有什么可以阻止另一个线程修改该值。由于 .NET 中的变量访问是原子的(请参见下面的警告),因此锁实际上并没有实现太多目的:只是增加了开销。与解锁示例对比:
另一个线程可能会更改步骤 1 和步骤 2 之间的值,这与锁定示例没有什么不同。
如果要确保在进行某些处理时状态不会更改,则必须读取该值并在锁的上下文中使用该值进行处理:
话虽如此,在某些情况下,访问变量时需要锁定。这些通常是由于底层处理器的原因造成的:例如,在 32 位机器上无法将
double
变量作为单个指令读取或写入,因此您必须锁定(或使用替代策略)以确保不读取损坏的值。The locks, as you have written them are pointless. The thread reading the variable, for example, will:
There is nothing to stop another thread from modifying the value after step 3. As variable access in .NET is atomic (see caveat below), the lock is not actually achieving much here: merely adding an overhead. Contrast with the unlocked example:
Another thread may alter the value between step 1 and 2 and this is no different to the locked example.
If you want to ensure state does not change when you are doing some processing, you must read the value and do the processing using that value within the contex of the lock:
Having said that, there are cases when you need to lock when accessing a variable. These are usually due to reasons with the underlying processor: a
double
variable cannot be read or written as a single instruction on a 32 bit machine, for example, so you must lock (or use an alternative strategy) to ensure a corrupt value is not read.由于您有一个原始值,因此锁定将正常工作 - 另一个问题中的问题是属性值是一个更复杂的类(可变引用类型) - 锁定将保护访问和检索由你的班级。
另一方面,如果您的属性值是可变引用类型,则锁定将无法防止使用类实例的方法检索后更改类实例,而这正是另一张海报所希望的。
Since you have a primitive value this locking will work fine - the issue in the other question was that the property value was a more complex class (a mutable reference type) - the locking will protect accessing and retrieving the instance of the double value held by your class.
If your property value is a mutable reference type on the other hand locking will not protect from changing the class instance once retrieved using its methods, which is what the other poster wanted it to do.
线程安全不是您应该添加到变量中的东西,而是您应该添加到“逻辑”中的东西。如果你为所有变量添加锁,你的代码仍然不一定是线程安全的,但它会非常慢。
要编写线程安全程序,请查看代码并确定多个线程可以在何处使用相同的数据/对象。在所有这些关键地方添加锁或其他安全措施。
例如,假设以下伪代码:
如果同时从多个线程调用此代码,则您的锁定逻辑没有用处。想象一下线程 A 获取 AvgBuyPrice 并进行一些计算。现在,在完成之前,线程 B 也正在获取 AvgBuyPrice 并开始计算。与此同时,线程 A 完成并将新值分配给 AvgBuyPrice。然而,不久之后,它就会被线程 B 覆盖(它仍然使用旧值),并且线程 A 的工作已经完全丢失。
那么如何解决这个问题呢?如果我们要使用锁(这将是最丑陋和最慢的解决方案,但如果您刚刚开始使用多线程,则是最简单的),我们需要将所有更改 AvgBuyPrice 的逻辑放入锁中:
现在,如果线程 B 想要执行以下操作当线程 A 仍然忙碌时,它将等待线程 A 完成,然后使用新值执行其工作。但请记住,任何其他也修改 AvgBuyPrice 的代码也应该在 AvgBuyPriceLocker 工作时锁定它!
不过,如果经常使用的话,速度会很慢。锁是昂贵的,并且有很多其他机制可以避免锁,只需搜索无锁算法即可。
Thread safety is not something you should add to your variables, it is something you should add to your "logic". If you add locks to all your variables, your code will still not necessarily be thread safe, but it will be slow as hell.
To write a thread-safe program, Look at your code and decide where multiple threads could be using the same data/objects. Add locks or other safety measures to all those critical places.
For instance, assuming the following bit of pseudo code:
If this code is called from multiple threads at the same time, your locking logic has no use. Imagine thread A getting AvgBuyPrice and doing some calculations. Now before it is done, thread B is also getting the AvgBuyPrice and starting calculations. Thread A in the meantime is done and will assign the new value to AvgBuyPrice. However, just moments later, it will be overwritten by thread B (which still used the old value) and the work of thread A has been lost completely.
So how do you fix this? If we were to use locks (which would be the ugliest and slowest solution, but the easiest if you're just starting with multithreading), we need to put all the logic which changes AvgBuyPrice in locks:
Now, if thread B wants to do the calculations while thread A is still busy, it will wait until thread A is done and then do its work using the new value. Keep in mind though, that any other code that also modifies AvgBuyPrice should also lock AvgBuyPriceLocker while it's working!
Still, this will be slow if used often. Locks are expensive and there are a lot of other mechanism to avoid locks, just search for lock-free algorithms.
无论如何,双精度数的读写都是原子的(源)双精度数的读取和写入不是原子性的,因此有必要使用锁来保护对双精度数的访问,但是对于许多类型来说,读取和写入是原子性的,因此以下内容只是安全:我的观点是,线程安全比简单地保护每个属性更复杂。举一个简单的例子,假设我有两个属性
AvgBuyPrice
和StringAvgBuyPrice
:假设我这样更新平均购买价格:
这显然不是线程安全的,并且单独保护属性上述方法根本没有帮助。在这种情况下,锁定应该在不同的级别而不是在每个属性级别执行。
Reading and writing of doubles is atomic anyway (source)reading and writing of doubles isn't atomic and so it would be necessary to protect access to a double using a lock, however for many types reading and writing is atomic and so the following would be just as safe:My point is that thread safety is more complex than simply protecting each of your properties. To give a simple example suppose I have two properties
AvgBuyPrice
andStringAvgBuyPrice
:And suppose I update the average buy price thusly:
This clearly isn't thread safe and individually protecting properties in the above way won't help at all. In this case the locking should be performed at a different level rather than at a per-property level.
虽然是一个老问题,但它出现在谷歌搜索的顶部,所以我添加了一个回复。在您的示例中,
get
储物柜在return
后不会被释放。因此,我建议在这种情况下在try-finally
块中使用ReaderWriterLockSlim
,这非常适合您尝试实现的结果。它允许多个线程进行读取或独占访问进行写入:Although an old question, it gets on top of Google searches, so I add a reply. In you example, the
get
locker will not be released afterreturn
. Therefore, I suggest to use theReaderWriterLockSlim
within atry-finally
block in such a case, which is very well suitable for the result you try to accomplish. It allows multiple threads for reading or exclusive access for writing: