簡體   English   中英

更改排他鎖定對象時的奇怪行為-Monitor.Enter(x)

[英]Strange Behavior When Changing Exclusively Locked Object - Monitor.Enter(x)

我想看看如果您更改由Monitor.Enter()專門鎖定的對象的引用會發生什么。 如預期的那樣,引發了SynchronizationLockException。 但是令我驚訝的是,在引發異常之前,有多個線程通過了Monitor。

這是下面的代碼在做什么。

  1. 創建並啟動100個線程
  2. 使所有線程等待,直到設置了ManualResetEvent。
  3. 設置ManualResetEvent-Kinda就像在印地比賽中揮舞綠色旗幟一樣。
  4. 在x上設置排他鎖(Monitor.Enter(x))
  5. 更改x的參考。

在這一點上,我預計會引發某種異常,但是直到Monitor.Exit(x)都不會發生。 真正奇怪的是,在引發異常之前,似乎有10到20個線程能夠通過鎖。 怎么發生的? 似乎不應該。 當然,更改排他鎖定對象的參考是不可以的。 我永遠不會在真實的代碼中做到這一點,但是仍然令我驚訝的是,其他線程已經超過了監視器。 你的意見?

using System;
using System.Threading;

namespace ThreadingPlayground
{
  class Program
  {
    private int _value;
    object x = new object();

    ManualResetEvent _event = new ManualResetEvent(false);

    static void Main()
    {
      Program p = new Program();
      p.StartThreads();
      Console.ReadLine();
    }

    private void StartThreads()
    {

      for(int i = 0;i<100;i++)
      {
        Thread t = new Thread(IncrementValue);
        t.Start();
      }
      _event.Set();
    }

    private void IncrementValue()
    {
      WaitHandle.WaitAll(new WaitHandle[] {_event});

      Monitor.Enter(x);

      // Change the reference of the exclusively locked object. This 
      // allows other threads to enter, should this be possible?
      x = Thread.CurrentThread.ManagedThreadId.ToString();

      Console.WriteLine(++_value);

      // throws a SynchronizationLockException 
      // but not until 10 - 20 more lines are written
      Monitor.Exit(x);
    }
  }
}

控制台輸出,看起來有些線程通過了監視器?

您所看到的是預期的行為。 用於將引用傳遞給Monitor.Enter()的實際變量沒有什么特別的。 更改引用不應阻止其他線程獲得排他鎖,因為變量具有新值,並且該引用未在任何地方鎖定。

您的問題與Exit ,因為調用Exit的線程在傳入的引用上沒有排它鎖。另一個線程可能對此有鎖,但是您在其中執行的線程卻沒有鎖。

如您所知,這就是為什么最好總是使用永遠不會更改其引用的變量進行鎖定。 如果您的資源變量可能更改,請使用新的引用。

這足夠清楚嗎?

“ x”是對對象的引用; 它不是對象本身。 當你做

      x = Thread.CurrentThread.ManagedThreadId.ToString();

您已經丟棄了x先前引用的鎖定對象,並使x引用了其他某個對象。 現在當你做

      Monitor.Exit(x);

之所以會發生異常,是因為該對象實際上並未鎖定-您鎖定的對象現在是由垃圾收集器收集的垃圾。

出現這種情況的原因是您要在此處更改x的值:

x = Thread.CurrentThread.ManagedThreadId.ToString();

所以當你去

Monitor.Exit(x)

您正在使用其他對象釋放鎖。 就像您將一個帶鑰匙的掛鎖,然后嘗試從另一個掛鎖中取出帶鑰匙的掛鎖一樣。

此外,與其他控制台相比,Console.Writeline是一條昂貴的指令,因此有多個線程必須先進入Monitor,然后其中一個才能接近終點。

這是一個示例運行:

您啟動了一堆線程。

  • 線程T1獲得對象x的鎖。
  • T2嘗試獲取對象x鎖。 這個線程運氣不好,因為我們知道它將永遠等待。
  • T1改變x 現在它是一個新對象。 我稱它為x'1
  • T3嘗試獲取鎖。 但是變量x實際上引用了對象x'1 沒有人鎖定x'1 ,所以他通過了。
  • T3改變x 現在是一個名為x'3的新對象。
  • T1寫入控制台。
  • T3寫入控制台。
  • T1嘗試使用變量x釋放鎖定,該變量指向對象... x'3
  • Monitor對象說:“嘿,您認為您在做什么?您沒有那個鎖!吃這個例外小吸盤”

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM