【问题标题】:How to implement my own locking mechanism如何实现我自己的锁定机制
【发布时间】:2011-09-28 20:53:55
【问题描述】:

对于一个任务(如果你想知道它是为了并发)——我必须实现我自己的锁

(更具体地说:一个 TaS、一个 TTas 和一个 Array-Lock,如“多处理器编程的艺术”中所述)

我尝试了一些在线测试输入和输出方案(可惜他们需要很长时间才能尝试)。

你的程序是计算通过某个测试的 9 位数字

(它在荷兰语中被称为 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();

但每次我得到的结果都略有不同。

有什么明显的我做错了吗?

【问题讨论】:

  • 您也可以从 codereview.stackexchange.com 中受益。
  • @0a0d 因为这是一个实际问题,所以不是评论问题。
  • 这只能在单核CPU上工作。您缺少确保所有内核都拥有最新的内存视图的内存屏障。原子性是不够的。这本书几岁了?
  • @HansPassant:亚马逊说它是在 2008 年出版的。amazon.com/Art-Multiprocessor-Programming-Maurice-Herlihy/dp/…

标签: c# .net concurrency locking


【解决方案1】:

汉斯是对的。您的原子 get-and-set 布尔值似乎是正确的 - 事实上,在我看来,它似乎有点过度设计。并且锁似乎也正确,因为您已经为自己构建了一个可能非常低效的“自旋锁”。 (也就是说,所有等待的线程只是紧紧地坐在那里问“我可以走吗?我可以走吗?”而不是在轮到它们之前睡觉。)

不正确的是,您的锁不能保证任何两个都具有“counter”视图的线程具有 一致“counter”视图。两个线程可能在不同的处理器上,并且这些不同的处理器在它们的缓存中可能有不同的“计数器”副本。缓存的副本将被更新,并且只是偶尔写回主内存,因此实​​际上“丢失”了一些增加。

在 C# 中锁定的真正实现确保施加了一个完整的内存屏障,以便读取和写入不能“在时间上向前和向后”移动穿过围栏。这给了处理器一个提示,他们不需要那么聪明地缓存“计数器”。

【讨论】:

  • 我不确定我们的实现是否必须使用自旋锁,但这是书中提到的方式。无论如何,谢谢你的回答!我现在更好地理解了这项任务。我想我应该多了解一下内存屏障,并找到一种方法来有效地划分线程范围。
  • @user969527:当您使用它时,请查看 C# 规范中描述“易失性”语义的位以及关于线程启动、锁定等关键点的执行顺序的保证, 等等。这应该可以帮助你做对。我写的这个简短的概述也可能会有所帮助:blogs.msdn.com/b/ericlippert/archive/2011/06/16/…
  • @user969527:Vance Morrison 的文章也可能对您有所帮助。 msdn.microsoft.com/en-us/magazine/cc163715.aspx
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-08
  • 2021-05-23
  • 1970-01-01
  • 2013-02-13
  • 2018-10-05
  • 2012-08-15
相关资源
最近更新 更多