C# 读写同步类的实现

C# 读写同步类的实现,c#,.net,synchronization,readwritelock,C#,.net,Synchronization,Readwritelock,我正在写一个读写同步类,想知道下一步该怎么做。由于某种原因,它有时允许 Read 发生在代码< >写/代码>的中间,我找不到原因。 这就是我想从这门课上学到的: 不允许在写入的同时读取 多次读取可以同时发生 一次只能进行一次写入 当需要写入时,所有已执行的读取将继续, 当所有读取完成时,不允许新的读取,写入执行 我知道.Net framework有一个类来做这件事。。。但我想要的是理解和复制这样的东西。我不是在重新发明轮子,我是在试图通过制造我自己的轮子来理解它。。。碰巧我的轮子有点方了 我

我正在写一个读写同步类,想知道下一步该怎么做。由于某种原因,它有时允许<代码> Read <代码>发生在代码< >写/代码>的中间,我找不到原因。

这就是我想从这门课上学到的:

  • 不允许在写入的同时读取
  • 多次读取可以同时发生
  • 一次只能进行一次写入
  • 当需要写入时,所有已执行的读取将继续, 当所有读取完成时,不允许新的读取,写入执行
我知道.Net framework有一个类来做这件事。。。但我想要的是理解和复制这样的东西。我不是在重新发明轮子,我是在试图通过制造我自己的轮子来理解它。。。碰巧我的轮子有点方了

我目前拥有的是:

public class ReadWriteSync
{
    private ManualResetEvent read = new ManualResetEvent(true);
    private volatile int readingBlocks = 0;
    private AutoResetEvent write = new AutoResetEvent(true);
    private object locker = new object();

    public IDisposable ReadLock()
    {
        lock (this.locker)
        {
            this.write.Reset();
            Interlocked.Increment(ref this.readingBlocks);
            this.read.WaitOne();
        }

        return new Disposer(() =>
        {
            if (Interlocked.Decrement(ref this.readingBlocks) == 0)
                this.write.Set();
        });
    }

    public IDisposable WriteLock()
    {
        lock (this.locker)
        {
            this.read.Reset();
            this.write.WaitOne();
        }

        return new Disposer(() =>
        {
            this.read.Set();
            if (this.readingBlocks == 0)
                this.write.Set();
        });
    }

    class Disposer : IDisposable
    {
        Action disposer;
        public Disposer(Action disposer) { this.disposer = disposer; }
        public void Dispose() { this.disposer(); }
    }
}
这是我的测试程序。。。当出现问题时,它会以红色打印线条

class Program
{
    static ReadWriteSync sync = new ReadWriteSync();

    static void Main(string[] args)
    {
        Console.BackgroundColor = ConsoleColor.DarkGray;
        Console.ForegroundColor = ConsoleColor.Gray;
        Console.Clear();

        Task readTask1 = new Task(() => DoReads("A", 20));
        Task readTask2 = new Task(() => DoReads("B", 30));
        Task readTask3 = new Task(() => DoReads("C", 40));
        Task readTask4 = new Task(() => DoReads("D", 50));

        Task writeTask1 = new Task(() => DoWrites("E", 500));
        Task writeTask2 = new Task(() => DoWrites("F", 200));

        readTask1.Start();
        readTask2.Start();
        readTask3.Start();
        readTask4.Start();

        writeTask1.Start();
        writeTask2.Start();

        Task.WaitAll(
            readTask1, readTask2, readTask3, readTask4,
            writeTask1, writeTask2);
    }

    static volatile bool reading;
    static volatile bool writing;

    static void DoWrites(string name, int interval)
    {
        for (int i = 1; i < int.MaxValue; i += 2)
        {
            using (sync.WriteLock())
            {
                Console.ForegroundColor = (writing || reading) ? ConsoleColor.Red : ConsoleColor.Gray;
                writing = true;
                Console.WriteLine("WRITE {1}-{0} BEGIN", i, name);
                Thread.Sleep(interval);
                Console.WriteLine("WRITE {1}-{0} END", i, name);
                writing = false;
            }

            Thread.Sleep(interval);
        }
    }

    static void DoReads(string name, int interval)
    {
        for (int i = 0; i < int.MaxValue; i += 2)
        {
            using (sync.ReadLock())
            {
                Console.ForegroundColor = (writing) ? ConsoleColor.Red : ConsoleColor.Gray;
                reading = true;
                Console.WriteLine("READ {1}-{0} BEGIN", i, name);
                Thread.Sleep(interval * 3);
                Console.WriteLine("READ {1}-{0} END", i, name);
                reading = false;
            }

            Thread.Sleep(interval);
        }
    }
}
类程序
{
静态读写同步=新建读写同步();
静态void Main(字符串[]参数)
{
Console.BackgroundColor=ConsoleColor.DarkGray;
Console.ForegroundColor=ConsoleColor.Gray;
Console.Clear();
任务readTask1=新任务(()=>DoReads(“A”,20));
任务readTask2=新任务(()=>DoReads(“B”,30));
任务readTask3=新任务(()=>DoReads(“C”,40));
任务readTask4=新任务(()=>DoReads(“D”,50));
任务writeTask1=新任务(()=>DoWrites(“E”,500));
TaskWriteTask2=新任务(()=>Dowrite(“F”,200));
readTask1.Start();
readTask2.Start();
readTask3.Start();
readTask4.Start();
writeTask1.Start();
writeTask2.Start();
Task.WaitAll(
readTask1、readTask2、readTask3、readTask4、,
writeTask1、writeTask2);
}
静态挥发油读数;
静态易变布尔写;
静态void DoWrites(字符串名称,int间隔)
{
对于(int i=1;i

这一切有什么不对。。。有没有关于如何正确执行的建议?

我看到的主要问题是,您试图使重置事件包含读/写的含义和对其当前状态的处理,而不以一致的方式进行同步

下面是一个例子,说明了不一致的同步可能会对您的特定代码造成什么影响

  • 一个
    写入
    正在处理,一个
    读取
    正在进入
  • 读取
    获取锁
  • 写入
    设置
    读取
    手动重置事件(MRE)
  • write
    检查当前读取计数,结果为0
  • 读取
    重置
    写入
    自动重置事件(ARE)
  • 读取
    增加读取计数
  • read
    发现其MRE已设置并开始读取
到目前为止一切都很好,但是
写入
尚未完成

  • 第二个
    write
    进入并获取锁
  • 第二个
    write
    重置
    read
    MRE
  • 第一次
    写入
    通过设置
    写入
  • 第二次
    写入
    发现其已设置并开始写入
当考虑多个线程时,除非您处于某种类型的锁中,否则必须考虑所有其他数据都在剧烈波动,并且不可信

这种简单的实现可能会将排队逻辑从状态逻辑中分离出来,并进行适当的同步

    public class ReadWrite
    {
        private static int readerCount = 0;
        private static int writerCount = 0;
        private int pendingReaderCount = 0;
        private int pendingWriterCount = 0;
        private readonly object decision = new object();

        private class WakeLock:IDisposable
        {
            private readonly object wakeLock;
            public WakeLock(object wakeLock) { this.wakeLock = wakeLock; }
            public virtual void Dispose() { lock(this.wakeLock) Monitor.PulseAll(this.wakeLock); }
        }
        private class ReadLock:WakeLock
        {
            public ReadLock(object wakeLock) : base(wakeLock) { Interlocked.Increment(ref readerCount); }
            public override void Dispose()
            {
                Interlocked.Decrement(ref readerCount);
                base.Dispose();
            }
        }            
        private class WriteLock:WakeLock
        {
            public WriteLock(object wakeLock) : base(wakeLock) { Interlocked.Increment(ref writerCount); }
            public override void Dispose()
            {
                Interlocked.Decrement(ref writerCount);
                base.Dispose();
            }
        }

        public IDisposable TakeReadLock()
        {
            lock(decision)
            {
                pendingReaderCount++;
                while (pendingWriterCount > 0 || Thread.VolatileRead(ref writerCount) > 0)
                    Monitor.Wait(decision);
                pendingReaderCount--;
                return new ReadLock(this.decision);
            }
        }

        public IDisposable TakeWriteLock()
        {
            lock(decision)
            {
                pendingWriterCount++;
                while (Thread.VolatileRead(ref readerCount) > 0 || Thread.VolatileRead(ref writerCount) > 0)
                    Monitor.Wait(decision);
                pendingWriterCount--;
                return new WriteLock(this.decision);
            }
        }
    }

你能描述一下pendingReaderCount/pendingWriterCount文件的用途吗?我们真的需要它们吗?似乎我们总是增加readerCount/writerCount,因为在获取读/写锁时会有锁(决策)