使类的属性线程安全的最佳做法是什么
本文关键字:最佳 是什么 线程 属性 安全 | 更新日期: 2023-09-27 18:36:45
[编辑:看起来原始问题涉及双精度而不是整数。所以我认为如果我们把整数改成双精度,这个问题就成立。
从多个线程中使用的类中读取整数属性时,我很少遇到问题,该类有时会返回零值。初始化后不会更改这些值。
这个问题解决了这个问题。共识是,即使我正在访问一个整数,我也需要同步属性。(部分原答案已被删除)。我还没有在那里选择答案,因为我还没有解决我的问题。
所以我对此做了一些研究,我不确定要使用哪个 .Net 4 的锁定机制,或者锁是否应该在类本身之外。
这就是我考虑使用:
public class ConfigInfo
{
private readonly object TimerIntervalLocker = new object();
private int _TimerInterval;
public int TimerInterval
{
get
{
lock (TimerIntervalLocker) {
return _TimerInterval;
}
}
}
private int _Factor1;
public int Factor1
{
set
{
lock (TimerIntervalLocker) {
_Factor1 = value;
_TimerInterval = _Factor1 * _Factor2;
}
}
get
{
lock (TimerIntervalLocker) {
return _Factor1;
}
}
}
private int _Factor2;
public int Factor2
{
set
{
lock (TimerIntervalLocker) {
_Factor2 = value;
_TimerInterval = _Factor1 * _Factor2;
}
}
get
{
lock (TimerIntervalLocker) {
return _Factor2;
}
}
}
}
但我读到这太慢了。
另一种选择是在用户端锁定ConfigData
实例,但这似乎需要做很多工作。我见过的另一种选择是Monitor.Enter
和Monitor.Exit
,但我认为 Lock 是一回事,语法更少。
那么,创建类的属性线程的最佳实践是什么 安全?
可能很慢,因为它使用操作系统资源,如果属性的复杂性较低,则旋转锁(或互锁.compareexchange)会更快。
二.您必须确保线程不会进入锁,并且通过从一个属性到另一个属性的调用被锁定。- 如果发生这种情况(当前不是代码中的问题),则需要使锁线程或任务敏感。
编辑:
如果对象应该在初始化期间设置并且从未更改,请使其不可变(就像 .NET 字符串一样)。删除所有公共资源库,并为构造函数提供用于定义初始状态的参数,以及用于创建具有修改状态的新实例的其他方法/运算符(例如 var newString = "Old string" + " 被修改;)了)。
如果值永远不会更改,则更容易复制该实例并为每个线程传递自己的实例。完全不需要锁定。
我认为你应该重写你的ConfigInfo
类,看起来像这样;这样你就不会出现溢出或线程问题:
public sealed class ConfigInfo
{
public ConfigInfo(int factor1, int factor2)
{
if (factor1 <= 0)
throw new ArgumentOutOfRangeException("factor1");
if (factor2 <= 0)
throw new ArgumentOutOfRangeException("factor2");
_factor1 = factor1;
_factor2 = factor2;
checked
{
_timerInterval = _factor1*_factor2;
}
}
public int TimerInterval
{
get
{
return _timerInterval;
}
}
public int Factor1
{
get
{
return _factor1;
}
}
public int Factor2
{
get
{
return _factor2;
}
}
private readonly int _factor1;
private readonly int _factor2;
private readonly int _timerInterval;
}
请注意,我正在使用checked
来检测溢出问题。
否则,某些值将给出不正确的结果。
例如,57344 * 524288
将在未经检查的整数算术中给出零(还有许多其他值对将给出零,甚至更多的值将给出负结果或"看起来"正确的正值)。
如注释中所述,最好将属性设置为只读。我想到了以下可能性:
public class ConfigInfo
{
private class IntervalHolder
{
public static readonly IntervalHolder Empty = new IntervalHolder();
private readonly int _factor1;
private readonly int _factor2;
private readonly int _interval;
private IntervalHolder()
{
}
private IntervalHolder(int factor1, int factor2)
{
_factor1 = factor1;
_factor2 = factor2;
_interval = _factor1*_factor2;
}
public IntervalHolder WithFactor1(int factor1)
{
return new IntervalHolder(factor1, _factor2);
}
public IntervalHolder WithFactor2(int factor2)
{
return new IntervalHolder(_factor1, factor2);
}
public int Factor1
{
get { return _factor1; }
}
public int Factor2
{
get { return _factor2; }
}
public int Interval
{
get { return _interval; }
}
public override bool Equals(object obj)
{
var otherHolder = obj as IntervalHolder;
return
otherHolder != null &&
otherHolder._factor1 == _factor1 &&
otherHolder._factor2 == _factor2;
}
}
private IntervalHolder _intervalHolder = IntervalHolder.Empty;
public int TimerInterval
{
get { return _intervalHolder.Interval; }
}
private void UpdateHolder(Func<IntervalHolder, IntervalHolder> update)
{
IntervalHolder oldValue, newValue;
do
{
oldValue = _intervalHolder;
newValue = update(oldValue);
} while (!oldValue.Equals(Interlocked.CompareExchange(ref _intervalHolder, newValue, oldValue)));
}
public int Factor1
{
set { UpdateHolder(holder => holder.WithFactor1(value)); }
get { return _intervalHolder.Factor1; }
}
public int Factor2
{
set { UpdateHolder(holder => holder.WithFactor2(value)); }
get { return _intervalHolder.Factor2; }
}
}
这样,您的定时间隔值始终与其因子同步。唯一的问题是当某个线程读取其中一个属性,而另一个线程从 ConfigInformation 外部写入它们时。第一个可能会得到错误的值,如果不引入单个锁根,我看不到任何解决此问题的方法。问题是读取操作是否至关重要。