在多线程代码中,当一个实例可能被多个线程读取或写入时,它们需要被锁定以安全地执行这些操作。
为了避免重复创建要锁定的对象和通过代码编写一堆锁定语句,我创建了一个通用类来处理锁定。
从概念上讲,我是否遗漏了什么?这应该有效,对吧?
public class Locked<T> where T : new()
{
private readonly object locker = new object();
private T value;
public Locked()
: this(default(T))
{ }
public Locked(T value)
{
this.value = value;
}
public T Get()
{
lock (this.locker)
{
return this.value;
}
}
public void Set(T value)
{
lock (this.locker)
{
this.value = value;
}
}
}
还有一个在类里面使用它的例子:
private Locked<bool> stopWorkerThread = new Locked<bool>();
public void WorkerThreadEntryPoint()
{
while (true)
{
if (this.stopWorkerThread.Get())
{
break;
}
另外,我如何以自动化方式测试这样的东西(例如创建单元测试)?
最后,我可以做些什么来实现++ 和 -- 运算符,以避免这种情况:
this.runningThreads.Set(this.runningThreads.Get() + 1);
请您参考如下方法:
仅在获取/设置期间锁定;当然,在许多常见情况下,这无论如何都是原子的,仅仅是由于数据大小。
然而,实际上大多数锁需要跨越的范围不止于此,就像仅在 Add 等上锁定集合并没有多大帮助一样 - 调用者通常需要一个锁来跨越“它在那里吗?如果所以更新,否则添加“序列。
对于像 bool 这样简单的东西,“volatile”可能会更简单地解决问题 - 特别是如果它只是为了循环退出。
您可能还想考虑 [MethodImpl(MethodImplOptions.Synchronized)] - 尽管我个人更喜欢私有(private)锁对象(就像您使用的那样)以防止外部人员锁定对象的问题(上面使用“this”作为锁)。
对于此单元测试,您首先需要一些东西来证明它已损坏 - 这将很难,因为操作非常小(并且对于大多数数据类型来说已经是原子的)。它避免的另一件事(volatile 也修复)是在寄存器中缓存,但这又是一种优化,很难强制证明它被破坏了。
如果您对锁包装器感兴趣,您可以考虑像 this 这样的现有代码.
