سلوك غريب عند تغيير حصريا تأمين الكائن المونيتور.أدخل(x)

StackOverflow https://stackoverflow.com/questions/828014

  •  06-07-2019
  •  | 
  •  

سؤال

أردت أن أرى ماذا يحدث إذا قمت بتغيير مرجع كائن حصريا مغلق من جهاز العرض.أدخل().كما يتوقع SynchronizationLockException ألقيت.ولكن فوجئت أن نرى العديد من المواضيع الحصول على الماضي رصد قبل تم طرح استثناء.

هنا ما البرمجية أدناه يقوم به.

  1. و انشاء 100 المواضيع
  2. جعل كل موضوع الانتظار حتى ManualResetEvent تعيين.
  3. تعيين ManualResetEvent - كيندا مثل التلويح العلم الأخضر في سباق إندي.
  4. مجموعة حصرية قفل (مراقبة.أدخل(x)) على x
  5. تغيير x المرجعية.

عند هذه النقطة كنت أتوقع نوعا من الاستثناء أن يلقى ولكن هذا لا يحدث حتى مراقبة.خروج(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);
    }
  }
}

Console Output, looks like some threads got past the monitor??

هل كانت مفيدة؟

المحلول

ما ترونه هو السلوك المتوقع.لا يوجد شيء خاص حول الفعلية المتغيرة تستخدم لتمرير إشارة إلى Monitor.Enter().تغيير الإشارة لا ينبغي أن يمنع المواضيع الأخرى من الحصول على تأمين للاستخدام خاص ، متغير يحتوي على قيمة جديدة ، و أن الإشارة لا تخوض في أي مكان.

المشكلة تأتي مع Exit, لأن موضوع الدعوة Exit لا يكون حصري قفل على المرجعية التي يتم تمريرها في.موضوع آخر قد قفل ، ولكن موضوع أنت المنفذة في لا.

وهذا كما تعلمون هو السبب في أنه من الأفضل دائما أن تفعل قفل مع المتغير الذي المرجعية لن يتغير أبدا.إذا كان المورد المتغير قد تغير ، استخدم إشارة جديدة.

هذا واضح بما فيه الكفاية ؟

نصائح أخرى

"x" هو إشارة إلى كائن ؛ ليس هدف في حد ذاته.عندما تفعل

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

كنت قد ألقيت بعيدا مؤمن كائن x المشار إليها سابقا ، وجعل x الرجوع إلى بعض وجوه أخرى.الآن عندما تفعل

      Monitor.Exit(x);

يمكنك الحصول على استثناء لأن هذا الكائن ليست في الواقع تأمين الكائن التي تخوض الآن القمامة التي يتم جمعها من قبل هواة جمع القمامة.

سبب هذا السلوك هو انك تغير قيمة x هنا:

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

لذلك عندما تحصل على

Monitor.Exit(x)

كنت الافراج عن قفل مع وجوه مختلفة.كما لو كنت وضعت قفل مع مفتاح واحد ، ومحاولة إزالة القفل مع المفتاح من القفل.

بالإضافة إلى وحدة التحكم.Writeline هو تكلفة التعليم مقارنة مع الآخرين ، لذلك العديد من المواضيع تحصل على إدخال الشاشة قبل واحد منهم يقترب من خط النهاية.

هنا مثال على تشغيل:

تبدأ مجموعة من المواضيع.

  • الموضوع 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 مع الإسناد
لا تنتمي إلى StackOverflow
scroll top