【问题标题】:Exception thrown using Monitor.Exit使用 Monitor.Exit 引发的异常
【发布时间】:2020-08-07 18:19:49
【问题描述】:

我似乎无法弄清楚为什么会出现此异常。 这是我正在运行的代码和我尝试过的事情。

public static Boolean IsRunning = false;

public static void Process()
{
    Boolean lockTaken = false;
    try
    {
        Monitor.TryEnter(IsRunning, ref lockTaken);
        if (lockTaken)
        {
            // Run code
        }
    }
    catch (Exception e) { e.Log(); }
    finally
    {
        if (lockTaken)
        {
            Monitor.Exit(IsRunning);
        }
    }
}

由于某种原因,我在Monitor.Exit(IsRunning); 行抛出了异常,这很奇怪,因为如果监视器成功获得IsRunning 对象的锁定,我只会运行该行代码。有什么可能导致抛出此异常的线索吗?

抛出的异常是对象同步方法是从不同步的代码块中调用的。

【问题讨论】:

  • 你没有指定抛出什么异常。
  • @PhonicUK 不需要指定异常的类型。 IsRunning 的类型是 System.Boolean,所以很明显是 SynchronizationLockException :)

标签: c# clr


【解决方案1】:

嗯...属性IsRunningSystem.Boolean 类型,它是值类型或结构。您只能在引用类型的实例上设置锁定。

在这里你可以阅读Monitor类:https://docs.microsoft.com/en-us/dotnet/api/system.threading.monitor?view=netcore-3.1

您可以在此处阅读有关值和引用类型的信息:https://www.tutorialsteacher.com/csharp/csharp-value-type-and-reference-type

当您将值类型的实例作为System.Object 或通过接口传递给方法时,就会发生装箱分配。

这是什么?

CLR 分配内存,然后将传递的实例的值复制到堆中,并将对分配对象的引用真正传递给被调用的方法。 因此,当您调用Enter 时,CLR 会分配一个新对象并将它的引用传递给该方法。当您调用Exit 时,它会再分配一个新对象,它是另一个 对象。所以当前线程没有锁定它。这就是引发异常的原因。

您可以在此处阅读有关装箱和拆箱的信息:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing#:~:text=Boxing%20is%20the%20process%20of,value%20type%20from%20the%20object

【讨论】:

    【解决方案2】:

    Serafim Prozorov 已经在这里解释了这个问题,但是:你让自己的生活变得艰难。在所示场景中,您没有使用任何超时等,这意味着您的代码在语义上与使用 lock 相同:

    try
    {
        lock (IsRunning)
        {
            // Run code 
        }
    }
    catch (Exception e) { e.Log(); }
    

    之所以相关是因为上面的代码不能编译;它说:

    CS0185 'bool' 不是 lock 语句所要求的引用类型

    这会阻止你犯这个错误。

    它还发现了其他场景,直接使用Monitor没有发现,例如await(CS1996,不能在锁语句的主体中等待)。


    还要注意Monitorlock 都不会更改您的bool 以记录它正在运行。有两种常见的方法:

    1. 有同步和“我在做什么吗?”分开,并根据需要更新后者
    2. 使用int 字段,并使用Interlocked.CompareExchange 将其在0 和1 之间更改;例如:if (Interlocked.CompareExchange(ref active, 1, 0) == 0) 从非活动 (0) 移动到活动 (0) 当且仅当它是非活动的,表达式(包括 ==0)返回 true在这种情况下,或false 否则(即它已经处于活动状态)

    【讨论】:

      猜你喜欢
      • 2018-04-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-25
      • 1970-01-01
      相关资源
      最近更新 更多