我正在试验不需要原子指令的锁。彼得森的算法似乎是最简单的起点。然而,经过足够的迭代,出现了一些问题。
代码:

public class Program
{
    private static volatile int _i = 0;
    public static void Main(string[] args)
    {
        for (int i = 0; i < 1000; i++)
        {
            RunTest();
        }
        Console.Read();
    }

    private static void RunTest()
    {
        _i = 0;
        var lockType = new PetersonLock();
        var t1 = new Thread(() => Inc(0, lockType));
        var t2 = new Thread(() => Inc(1, lockType));

        t1.Start();
        t2.Start();

        t1.Join();
        t2.Join();
        Console.WriteLine(_i);
    }

    private static void Inc(int pid, ILock lockType)
    {
        try
        {
            for (int i = 0; i < 1000000; i++)
            {
                lockType.Request(pid);
                _i++;
                lockType.Release(pid);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}

public interface ILock
{
    void Request(int pid);
    void Release(int pid);
}

public class NoLock : ILock
{
    public void Request(int pid) { }
    public void Release(int pid) { }
}

public class StandardLock : ILock
{
    private object _sync = new object();

    public void Request(int pid)
    {
        Monitor.Enter(_sync);
    }

    public void Release(int pid)
    {
        Monitor.Exit(_sync);
    }
}

public class PetersonLock : ILock
{
    private volatile bool[] _wantsCs = new bool[2];
    private volatile int _turn;

    public void Request(int pid)
    {
        int j = pid == 1 ? 0 : 1;
        _wantsCs[pid] = true;
        _turn = j;
        while (_wantsCs[j] && _turn == j)
        {
            Thread.Sleep(0);
        }
    }

    public void Release(int pid)
    {
        _wantsCs[pid] = false;
    }
}

当我运行这个时,我总是得到

最佳答案

这里的问题是这两种说法:

_wantsCs[pid] = true;
_turn = j;

.net和c的内存模型允许对这两个写操作重新排序。
要强制它们不重新排序,请在它们之间添加一个内存屏障:
_wantsCs[pid] = true;
Thread.MemoryBarrier();
_turn = j;

每次都会得到预期的输出。
请注意,这个问题在注释部分的Wikipedia page for Peterson's Algorithm中有描述(以下简称,请阅读文章以获取完整的注释):
笔记

大多数现代CPU都会对内存访问重新排序以提高执行效率(有关允许的重新排序类型,请参阅内存排序)。这种处理器总是提供某种方式来强制内存访问流中的顺序,通常是通过内存屏障指令。在对内存访问重新排序的处理器上实现peterson和相关算法通常需要使用这些操作才能正常工作,以防止顺序操作以错误的顺序发生。
(我强调)

08-15 19:34