يقوم مُنشئ المثيل بتعيين عضو ثابت، هل هو آمن لمؤشر الترابط؟

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

سؤال

أقوم بإعادة تحليل بعض التعليمات البرمجية وأتساءل عن استخدام ملف lock في منشئ المثيل.

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

        lock(this) {
            counter++;
            myCount = counter;
        }
    }
}

يرجى تأكيد

  1. منشئو المثيلات آمنون للخيوط.
  2. تمنع عبارة القفل الوصول إلى كتلة التعليمات البرمجية هذه، وليس إلى العضو "العداد" الثابت.

إذا كان هدف المبرمج الأصلي هو جعل كل مثيل يعرف "العدد" الخاص به، فكيف يمكنني مزامنة الوصول إلى العضو "العداد" للتأكد من أن مؤشر ترابط آخر ليس جديدًا MyClass وتغيير العد قبل أن يحدد هذا العد؟

لمعلوماتك - هذه الفئة ليست مفردة.يجب أن تكون المثيلات ببساطة على علم بعددها.

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

المحلول

@ajmasterean

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

أي.

  • اجعل المنشئ خاصًا.
  • قم بإنشاء طريقة مثيل ثابتة تقوم بإرجاع النوع.
  • في طريقة المثيل الثابت، استخدم الكلمة الأساسية lock قبل إنشاء مثيل.
  • إنشاء مثيل جديد من النوع.
  • زيادة العدد.
  • فتح وإرجاع المثيل الجديد.

يحرر

إحدى المشاكل التي حدثت لي، كيف يمكنك معرفة متى انخفض العدد؟;)

تحرير مرة أخرى

بالتفكير في الأمر، يمكنك إضافة تعليمات برمجية إلى أداة التدمير التي تستدعي طريقة ثابتة أخرى لتقليل العداد:D

نصائح أخرى

إذا كنت تقوم بزيادة رقم فقط، فهناك فئة خاصة (متشابكة) لذلك...

http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment.aspx

طريقة Interlocked.Increment

زيادة متغير محدد وتخزين النتيجة، كعملية ذرية.

System.Threading.Interlocked.Increment(myField);

مزيد من المعلومات حول أفضل ممارسات الترابط...

http://msdn.microsoft.com/en-us/library/1c9txz50.aspx

أظن أن هذا لنمط فردي أو شيء من هذا القبيل.ما تريد القيام به ليس قفل الكائن الخاص بك، ولكن قفل العداد أثناء قيامك بتعديله.

private static int counter = 0;
private static object counterLock = new Object();

lock(counterLock) {
    counter++;
    myCounter = counter;
}

لأن الكود الحالي الخاص بك هو نوع من زائدة عن الحاجة.لا سيما أن تكون في المُنشئ حيث يوجد مؤشر ترابط واحد فقط يمكنه استدعاء المُنشئ، على عكس الطرق حيث يمكن مشاركته عبر سلاسل الرسائل ويمكن الوصول إليه من أي مؤشر ترابط تمت مشاركته.

من القليل الذي يمكنني معرفته من الكود الخاص بك، فأنت تحاول إعطاء الكائن العدد الحالي في وقت إنشائه.لذا، باستخدام الكود أعلاه، سيتم قفل العداد أثناء تحديث العداد وضبطه محليًا.لذلك سيتعين على جميع المنشئين الآخرين الانتظار حتى يتم تحرير العداد.

يمكنك استخدام كائن ثابت آخر لقفله.

private static Object lockObj = new Object();

وقفل هذا الكائن في المنشئ.

lock(lockObj){}

ومع ذلك، لست متأكدًا مما إذا كانت هناك مواقف يجب معالجتها بسبب تحسين المترجم .NET كما في حالة جافا

الطريقة الأكثر فعالية للقيام بذلك هي استخدام عملية الزيادة المتشابكة.سيزيد العداد ويعيد القيمة المحددة حديثًا للعداد الثابت مرة واحدة (ذريًا)

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

    public MyClass() { 
        this.instanceId = Interlocked.Increment(ref _LastInstanceId);  
    }
}

في المثال الأصلي الخاص بك، لن يكون لعبارة lock(this) التأثير المطلوب لأن كل مثيل فردي سيكون له مرجع "هذا" مختلف، وبالتالي يمكن لمثيلات متعددة تحديث العضو الثابت في نفس الوقت.

بمعنى ما، يمكن اعتبار المنشئات آمنة لمؤشر الترابط لأن المرجع إلى الكائن الذي يتم إنشاؤه غير مرئي حتى يكتمل المنشئ، ولكن هذا لا يفيد في حماية متغير ثابت.

(كان لدى مايك شال القطعة المتشابكة أولاً)

أعتقد أنه إذا قمت بتعديل نمط سينجلتون لتضمين عدد (من الواضح باستخدام الطريقة الآمنة للخيط)، ستكون بخير :)

يحرر

حماقة قمت بحذفها عن طريق الخطأ!

لست متأكدًا مما إذا كان مُنشئو المثيلات نكون آمن للخيط، أتذكر أنني قرأت عن هذا في كتاب أنماط التصميم، فأنت بحاجة إلى التأكد من وجود الأقفال في مكانها أثناء عملية إنشاء مثيل، ولهذا السبب فقط..

@روب

لمعلوماتك، قد لا يكون هذا الفصل فرديًا، فأنا بحاجة إلى الوصول إلى مثيلات مختلفة.يجب عليهم ببساطة الحفاظ على العد.ما هو جزء من النمط المفرد الذي تريد تغييره لإجراء زيادة "عدادية"؟

أم أنك تقترح أن أقوم بكشف طريقة ثابتة للبناء لمنع الوصول إلى الكود الذي يزيد ويقرأ العداد بقفل.

public MyClass {

    private static Int32 counter = 0;
    public static MyClass GetAnInstance() {

        lock(MyClass) {
            counter++;
            return new MyClass();
        }
    }

    private Int32 myCount;
    private MyClass() {
        myCount = counter;
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top