如何在writer尝试输入写锁定时避免阻塞ReaderWriterLockSlim读取器

Gab*_*ini 9 .net multithreading readerwriterlockslim

我正在ReaderWriterLockSlim用来保护一些操作.我想赞成读者而不是作家,这样当读者长时间持有锁并且作家试图获得写锁时,未来的读者也不会被作者的尝试所阻挡(如果是这样的话会发生这种情况.作家被封锁了lock.EnterWriteLock()).

为此,我虽然编写器可以TryEnterWriteLock在循环中使用短暂的超时,以便后续的读者仍然能够获得读锁定而编写者不能.然而,令我惊讶的是,我发现一个不成功的呼叫TryEnterWriteLock改变了锁的状态,无论如何阻止了未来的读者.概念证明代码:

System.Threading.ReaderWriterLockSlim myLock = new System.Threading.ReaderWriterLockSlim(System.Threading.LockRecursionPolicy.NoRecursion);

System.Threading.Thread t1 = new System.Threading.Thread(() =>
{
    Console.WriteLine("T1:{0}: entering read lock...", DateTime.Now);
    myLock.EnterReadLock();
    Console.WriteLine("T1:{0}: ...entered read lock.", DateTime.Now);

    System.Threading.Thread.Sleep(10000);
});

System.Threading.Thread t2 = new System.Threading.Thread(() =>
{
    System.Threading.Thread.Sleep(1000);

    while (true)
    {
        Console.WriteLine("T2:{0}: try-entering write lock...", DateTime.Now);
        bool result = myLock.TryEnterWriteLock(TimeSpan.FromMilliseconds(1500));
        Console.WriteLine("T2:{0}: ...try-entered write lock, result={1}.", DateTime.Now, result);

        if (result)
        {
            // Got it!
            break;
        }

        System.Threading.Thread.Yield();
    }

    System.Threading.Thread.Sleep(9000);
});

System.Threading.Thread t3 = new System.Threading.Thread(() =>
{
    System.Threading.Thread.Sleep(2000);

    Console.WriteLine("T3:{0}: entering read lock...", DateTime.Now);
    myLock.EnterReadLock();
    Console.WriteLine("T3:{0}: ...entered read lock!!!!!!!!!!!!!!!!!!!", DateTime.Now);

    System.Threading.Thread.Sleep(8000);
});
Run Code Online (Sandbox Code Playgroud)

此代码的输出是:

T1:18-09-2015 16:29:49: entering read lock...
T1:18-09-2015 16:29:49: ...entered read lock.
T2:18-09-2015 16:29:50: try-entering write lock...
T3:18-09-2015 16:29:51: entering read lock...
T2:18-09-2015 16:29:51: ...try-entered write lock, result=False.
T2:18-09-2015 16:29:51: try-entering write lock...
T2:18-09-2015 16:29:53: ...try-entered write lock, result=False.
T2:18-09-2015 16:29:53: try-entering write lock...
T2:18-09-2015 16:29:54: ...try-entered write lock, result=False.
T2:18-09-2015 16:29:54: try-entering write lock...
T2:18-09-2015 16:29:56: ...try-entered write lock, result=False.
T2:18-09-2015 16:29:56: try-entering write lock...
T2:18-09-2015 16:29:57: ...try-entered write lock, result=False.
T2:18-09-2015 16:29:57: try-entering write lock...
T2:18-09-2015 16:29:59: ...try-entered write lock, result=False.
T2:18-09-2015 16:29:59: try-entering write lock...
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,即使线程2("Writer")没有获得写入锁定而且它不在EnterWriteLock调用中,线程3 也会被阻止.我可以看到类似的行为ReaderWriterLock.

我做错了吗?如果没有,当作家排队时,我有什么选择支持读者?

Mor*_*gil 3

我可以\xe2\x80\x99t 提供帮助,但我相信这是一个 .NET Framework 错误(更新:我已报告该错误)。以下简单的程序(上述程序的简化版本)说明了这一点:

\n\n
var myLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);\n\nvar t1 = new Thread(() =>\n{\n    Console.WriteLine("T1:{0}: entering read lock...", DateTime.Now);\n    myLock.EnterReadLock();\n    Console.WriteLine("T1:{0}: ...entered read lock.", DateTime.Now);\n\n    Thread.Sleep(50000);\n\n    Console.WriteLine("T1:{0}: exiting", DateTime.Now);\n    myLock.ExitReadLock();\n});\n\nvar t2 = new Thread(() =>\n{\n    Thread.Sleep(1000);\n\n    Console.WriteLine("T2:{0}: try-entering write lock...", DateTime.Now);\n    bool result = myLock.TryEnterWriteLock(3000);\n    Console.WriteLine("T2:{0}: ...try-entered write lock, result={1}.", DateTime.Now, result);\n\n    Thread.Sleep(50000);\n\n    if (result)\n    {\n        myLock.ExitWriteLock();\n    }\n    Console.WriteLine("T2:{0}: exiting", DateTime.Now);\n});\n\nvar t3 = new Thread(() =>\n{\n    Thread.Sleep(2000);\n\n    Console.WriteLine("T3:{0}: entering read lock...", DateTime.Now);\n    myLock.EnterReadLock();\n    Console.WriteLine("T3:{0}: ...entered read lock!!!!!!!!!!!!!!!!!!!", DateTime.Now);\n\n    Thread.Sleep(50000);\n\n    myLock.ExitReadLock();\n    Console.WriteLine("T3:{0}: exiting", DateTime.Now);\n});\n\nt1.Start();\nt2.Start();\nt3.Start();\n\nt1.Join();\nt2.Join();\nt3.Join();\n
Run Code Online (Sandbox Code Playgroud)\n\n

以下内容以简单的顺序发生,没有锁车队,没有竞赛,没有循环或任何东西。

\n\n
    \n
  1. T1获取读锁。
  2. \n
  3. T2尝试获取写锁并阻塞,等待超时(如T1持有锁)。
  4. \n
  5. T3尝试获取读锁并阻塞(因为T2被阻塞等待写锁,并且根据文档,这意味着所有其他读取器都被阻塞直到超时)。
  6. \n
  7. T2\xe2\x80\x99s 超时到期。根据文档,T3现在应该唤醒并获取读锁。然而,这种情况不会发生,并且T3会被永远阻塞(或者,在本示例中,在T1离开读锁之前的 50 秒内)。
  8. \n
\n\n

AFAICT,\xe2\x80\x99sExitMyLock中的应该是.ReaderWriterLockSlimWaitOnEventExitAndWakeUpAppropriateWaiters

\n

  • 好吧,你能解释一下为什么这样的程序(它的“T3”线程)应该无限期地阻塞吗?它在等待什么以及为什么?另外,我引用了上面的文档:_“阻塞直到所有等待进入写入模式的线程都超时或进入写入模式然后退出”_。它们已经超时,但线程仍然被阻塞。 (3认同)
  • @Hans:如果这不是一个错误,那么文档就会产生误导。从它的写法来看,请求写锁超时的线程和获取写锁然后退出的线程之间没有区别。在这两种情况下,您都希望任何排队的读取请求都会立即解除阻塞。在一种情况下确实会发生这种情况,但在另一种情况下则不会。我同意 Mormegil 的观点,即可能错过了对“ExitAndWakeUpAppropriateWaiters”的调用。 (2认同)