C# ReaderWriterLockSlim阻止读取,直到所有排队写入完成

C# ReaderWriterLockSlim阻止读取,直到所有排队写入完成,c#,multithreading,locking,thread-safety,C#,Multithreading,Locking,Thread Safety,我正在尝试使用ReaderWriterLockSlim类来管理列表 此列表有很多读取,很少写入,我的读取速度快,而写入速度慢 我编写了一个简单的测试工具来检查锁是如何工作的 如果出现以下情况: Thread 1 - Start Write Thread 2 - Start Read Thread 3 - Start Write 结果如下 Thread 1 starts its write and locks the list. Thread 2 adds itself to the read

我正在尝试使用
ReaderWriterLockSlim
类来管理列表

此列表有很多读取,很少写入,我的读取速度快,而写入速度慢

我编写了一个简单的测试工具来检查锁是如何工作的

如果出现以下情况:

Thread 1 - Start Write
Thread 2 - Start Read
Thread 3 - Start Write
结果如下

Thread 1 starts its write and locks the list.
Thread 2 adds itself to the read queue.
Thread 3 adds itself to the write queue.
Thread 1 finishes writing and releases the lock
Thread 3 aquires the lock and starts its write
Thread 3 finishes writing and releases the lock
Thread 2 performs its read
是否有任何方法可以更改锁的行为,以便在授予写锁之前允许在写锁之前排队的任何读取请求完成

编辑:下面是演示我遇到的问题的代码

public partial class SimpleLock : System.Web.UI.Page
{
    public static ReaderWriterLockSlim threadLock = new ReaderWriterLockSlim();

    protected void Page_Load(object sender, EventArgs e)
    {
        List<String> outputList = new List<String>();

        Thread thread1 = new Thread(
            delegate(object output)
            {
                ((List<String>)output).Add("Write 1 Enter");
                threadLock.EnterWriteLock();
                ((List<String>)output).Add("Write 1 Begin");
                Thread.Sleep(100);
                ((List<String>)output).Add("Write 1 End");
                threadLock.ExitWriteLock();
                ((List<String>)output).Add("Write 1 Exit");
            }
        );
        thread1.Start(outputList);

        Thread.Sleep(10);

        Thread thread2 = new Thread(
            delegate(object output)
            {
                ((List<String>)output).Add("Read 2 Enter");
                threadLock.EnterReadLock();
                ((List<String>)output).Add("Read 2 Begin");
                Thread.Sleep(100);
                ((List<String>)output).Add("Read 2 End");
                threadLock.ExitReadLock();
                ((List<String>)output).Add("Read 2 Exit");
            }
        );
        thread2.Start(outputList);

        Thread.Sleep(10);

        Thread thread3 = new Thread(
            delegate(object output)
            {
                ((List<String>)output).Add("Write 3 Enter");
                threadLock.EnterWriteLock();
                ((List<String>)output).Add("Write 3 Begin");
                Thread.Sleep(100);
                ((List<String>)output).Add("Write 3 End");
                threadLock.ExitWriteLock();
                ((List<String>)output).Add("Write 3 Exit");
            }
        );
        thread3.Start(outputList);

        thread1.Join();
        thread2.Join();
        thread3.Join();

        Response.Write(String.Join("<br />", outputList.ToArray()));
    }
}
public分部类SimpleLock:System.Web.UI.Page
{
public static ReaderWriterLockSlim threadLock=new ReaderWriterLockSlim();
受保护的无效页面加载(对象发送方、事件参数e)
{
List outputList=新列表();
螺纹螺纹1=新螺纹(
委托(对象输出)
{
((列表)输出)。添加(“写入1输入”);
threadLock.EnterWriteLock();
((列表)输出)。添加(“写入1开始”);
睡眠(100);
((列表)输出)。添加(“写入1结束”);
threadLock.ExitWriteLock();
((列表)输出)。添加(“写入1退出”);
}
);
thread1.开始(outputList);
睡眠(10);
螺纹螺纹2=新螺纹(
委托(对象输出)
{
((列表)输出)。添加(“读取2输入”);
threadLock.enterereadlock();
((列表)输出)。添加(“读取2开始”);
睡眠(100);
((列表)输出)。添加(“读取2结束”);
threadLock.exitradlock();
((列表)输出)。添加(“读取2退出”);
}
);
thread2.开始(outputList);
睡眠(10);
螺纹3=新螺纹(
委托(对象输出)
{
((列表)输出)。添加(“写入3输入”);
threadLock.EnterWriteLock();
((列表)输出)。添加(“写入3开始”);
睡眠(100);
((列表)输出)。添加(“写入3结束”);
threadLock.ExitWriteLock();
((列表)输出)。添加(“写入3退出”);
}
);
thread3.开始(outputList);
thread1.Join();
螺纹2.连接();
螺纹3.连接();
Write(String.Join(“
”,outputList.ToArray()); } }
是否有任何方法改变锁的性能,以便 允许在写入锁定之前排队的读取请求 是否在授予写锁之前完成

如何避免几乎完全使用锁?在写入过程中,您可以获取锁、复制原始数据结构、修改副本,然后通过用新引用替换旧引用来发布新数据结构。由于在数据结构“发布”后从不修改它,因此根本不需要锁定读取

以下是它的工作原理:

public class Example
{
  private object writelock = new object();
  private volatile List<string> data = new List<string>();

  public void Write(string item)
  {
    lock (writelock)
    {
      var copy = new List<string>(data); // Create the copy.
      copy.Add(item); // Modify the data structure.
      data = copy; // Publish the modified data structure.
    }
  }

  public string Read(int index)
  {
    return data[index];
  }
}
公共类示例
{
私有对象writelock=新对象();
私有易失性列表数据=新列表();
公共无效写入(字符串项)
{
锁(写锁)
{
var copy=新列表(数据);//创建副本。
copy.Add(item);//修改数据结构。
data=copy;//发布修改后的数据结构。
}
}
公共字符串读取(整数索引)
{
返回数据[索引];
}
}
我们在这里利用的技巧是
数据
变量引用的任何内容的不变性。我们需要做的唯一一件事是将变量标记为
volatile


请注意,只有在写入次数足够少且数据结构足够小以保持复制操作便宜的情况下,此技巧才有效。这不是最终的解决方案。它并不适用于所有场景,但可能只适用于您。

这是我们采用的方法。由于“列表”的大小很大,它实际上有巨大的开销。复印一份要花很长时间。然而,目前它似乎还有效。为了补充这一点,在您的示例中,在编写过程中,您创建了列表的第三个版本,然后用它替换snapshot。实际上,您只需维护“主”列表,所有读取都是从该列表中完成的。然后写操作可以“锁定”主机,这样就不会发生进一步的写操作。它的第一步是复制母版。然后它会根据需要更新副本,然后用副本替换“母版”。@RobinDay:是的,非常好!我没有想到这一点。这将大大减少内存消耗。为了反映您的想法,我对我的答案做了一些重大更改。您的问题的直接答案是“有没有任何方法可以更改锁的行为”,答案是否定的。文档中说,“如果有线程等待进入写入模式,或者如果有单个线程处于写入模式,则尝试进入读取模式的线程会被阻塞。”目前还没有改变这种行为的方法。