对于一个赋值(如果你想知道它是为了并发)——我必须实现我自己的锁
(更具体:TaS、TTas 和 Array-Lock,如“多处理器编程的艺术”中所述)
我尝试了一些在线测试输入和输出方案(太糟糕了,他们需要很长时间才能尝试)。
Your program is to count 9-digit numbers that pass a certain test
(它在荷兰语中被称为 elfproef,我不知道对应的英语,抱歉)。
有时我得到的数字略有不同,这表明我的锁不能100% 正确。
我已经实现了这样的锁:
interface Lock
{
void Lock();
void Unlock();
}
class TaSLock : Lock
{
AtomicBool state = new AtomicBool(false);
void Lock.Lock()
{ while (state.getAndSet(true)); }
void Lock.Unlock()
{ state.getAndSet(false); }
}
AtomicBool
是用integer
实现的,因为Interlocked
类没有Boolean
操作变量。这在内存使用方面不是最优的,但它对速度没有(或不应该)有影响。
class AtomicBool
{
int value;
static int True = 1, False = -1;
public AtomicBool(bool value)
{
if (value) this.value = True;
else this.value = False;
}
public void set(bool newValue)
{
if (newValue) Interlocked.Exchange(ref value, True);
else Interlocked.Exchange(ref value, False);
}
public bool getAndSet(bool newValue)
{
int oldValue;
if (newValue) oldValue = Interlocked.Exchange(ref value, True);
else oldValue = Interlocked.Exchange(ref value, False);
return (oldValue == True);
}
public bool get()
{
return (Interlocked.Add(ref value, 0) == 1);
}
}
现在在我刚刚使用的并行部分:
theLock.Lock();
counter++;
theLock.Unlock();
但每次我得到的结果都略有不同。
有什么明显的我做错了吗?
最佳答案
汉斯是对的。您的原子获取和设置 bool 值似乎是正确的——事实上,在我看来它有点过度设计。而且锁似乎也是正确的,因为您已经为自己构建了一个可能非常低效的“自旋锁”。 (也就是说,所有等待的线程都只是坐在那里,形成一个紧密的循环,询问“我可以走了吗?我可以走了吗?”而不是在轮到它们之前进入休眠状态。)
不正确的是,您的锁不能保证任何两个都具有“counter” View 的线程都具有一致“counter” View 。两个线程可能在不同的处理器上,而那些不同的处理器可能在它们的缓存中有不同的“计数器”副本。缓存的副本将被更新,并且只是偶尔写回主内存,从而有效地“丢失”了一些增加。
在 C# 中锁定的真正实现确保了一个完整的内存屏障被强加,这样读取和写入就不能“在时间上向前和向后”移动穿过栅栏。这给了处理器一个提示,即它们不需要如此聪明地如此积极地缓存“计数器”。
关于c# - 如何实现自己的锁定机制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7589469/