在 C# 中使用带有锁定重载的 Monitor 时出现问题

Issue when using Monitor with locktaken overload in C#

我正在测试带有锁过载的监视器,如下所示。

public static void Enter(object obj, ref bool lockTaken);

我为此创建了以下示例,但不确定什么是简单的修复方法。

class TestMonitor
    {
        int num1 = 0;
        int num2 = 0;
        Random rnd = new Random();
        private static object myLock = new object();
        bool isLockTaken = false;
        public void DoDivide()
        {
            try
            {
                Monitor.Enter(myLock, ref isLockTaken); //t2 fails here.
                {
                    for (int i = 0; i < 5; i++)
                    {
                        num1 = rnd.Next(1, 5);
                        num2 = rnd.Next(1, 5);
                        Console.WriteLine(num1 / num2);
                        num1 = 0;
                        num2 = 0;
                    }
                }
            }
            finally
            {
                if (isLockTaken)  { Monitor.Exit(myLock); }
            }
         }
    }

 class Program
    {        
        static void Main(string[] args)
        {
            Console.Title = "Monitor Demo";
            TestMonitor testThreadSafe = new TestMonitor();
            Thread t1 = new Thread(testThreadSafe.DoDivide);
            Thread t2 = new Thread(testThreadSafe.DoDivide);            
            t1.Start();
            t2.Start();  
        }
    }

当第二个线程 (t2) 尝试访问 Monitor.Enter(myLock, ref isLockTaken); 时出现错误,它发现 isLockTaken 为 true,尽管它期望 isLockTaken 为 false。 isLockTaken 由第一个线程 (t1) 生成,因为它已获得锁。我有点理解这个问题,但有人可以指出我的简单修复方法,以便两个线程都可以正常工作。

来自微软文档:

lockTaken Type: System.Boolean

The result of the attempt to acquire the lock, passed by reference. The input must be false. The output is true if the lock is acquired; otherwise, the output is false. The output is set even if an exception occurs during the attempt to acquire the lock.

由于您在 class 中声明了 isLockTaken,它将变为 true 并保持为 true。它应该进入 dodivide 函数。

一个简单的解决方法是完全不使用 isLockTaken 布尔值:

try
{
    Monitor.Enter(myLock);
    // ...
}
finally
{
    Monitor.Exit(myLock);
}

或者更好的是,使用 lock 语句:

lock(myLock)
{
    // ...
}

但是,如果您想使用 ref bool lockTaken 的重载,MSDN page 声明:

The input must be false.

执行此操作的简单方法是将 isLockTaken 字段移动到本地方法变量中。然后,由于每个线程都有自己的堆栈,它会得到自己的 isLockTaken 变量副本。

bool isLockTaken = false;
try
{
    Monitor.Enter(myLock, ref isLockTaken);
    // ...
}
finally
{
    if(isLockTaken)
    {
        Monitor.Exit(myLock);
    }
}