সি # স্ট্যাটিক কনস্ট্রাক্টর থ্রেড কি নিরাপদ?


247

অন্য কথায়, এই একা বাস্তবায়ন থ্রেড নিরাপদ:

public class Singleton
{
    private static Singleton instance;

    private Singleton() { }

    static Singleton()
    {
        instance = new Singleton();
    }

    public static Singleton Instance
    {
        get { return instance; }
    }
}

1
এটি থ্রেড-নিরাপদ। মনে করুন বেশ কয়েকটি থ্রেড Instanceএকবারে সম্পত্তি পেতে চায় । কোনও থ্রেডকে প্রথমে টাইপ ইনিশিয়ালাইজার চালাতে বলা হবে (এটি স্ট্যাটিক কনস্ট্রাক্টর নামেও পরিচিত)। ইতিমধ্যে Instanceসম্পত্তিটি পড়তে ইচ্ছুক অন্যান্য সমস্ত থ্রেড , টাইপ ইনিশিয়ালাইজারটি শেষ না হওয়া পর্যন্ত লক হয়ে যাবে। ফিল্ড ইনিশিয়ালাইজার শেষ হওয়ার পরে, থ্রেডগুলিকে Instanceমান পেতে দেওয়া হবে । সুতরাং কেউ Instanceসত্তা দেখতে পারে না null
জেপ্প স্টিগ নীলসন

@ জেপ্পস্টিগনিয়েলসন অন্যান্য থ্রেড লকড নেই। আমার নিজের অভিজ্ঞতা থেকে আমি খারাপ কারণে ত্রুটি পেয়েছি। গ্যারান্টিটি হ'ল কেবল মুষ্টির থ্রেডটি স্ট্যাটিক ইনিশিয়ালাইজার বা কনস্ট্রাক্টর শুরু করবে, তবে অন্য থ্রেডগুলি স্থির পদ্ধতি ব্যবহার করার চেষ্টা করবে, এমনকি নির্মাণ প্রক্রিয়াটি শেষ না হলেও।
নারভলেেক্স

2
@ নারভ্যালেক্স এই নমুনা প্রোগ্রামটি ( ইউআরএলটিতে এনকোডড উত্স) আপনার বর্ণিত সমস্যার পুনরুত্পাদন করতে পারে না। হয়তো এটি আপনার কাছে থাকা সিএলআরটির কোনও সংস্করণ নির্ভর করে?
জেপ্প স্টিগ নীলসন

@ জেপ্পস্টিগনিয়েলসন আপনার সময় দেওয়ার জন্য ধন্যবাদ। আপনি দয়া করে আমাকে এখানে ব্যাখ্যা করতে পারেন কেন এখানে ক্ষেত্রটি ওভাররেড করা হয়েছে?
নারভালেক্স

5
@ নারভ্যালেক্স সেই কোডটি সহ, উচ্চ-কেস থ্রেডিং ছাড়াইX শেষ হয় । এটি কোনও থ্রেড-সুরক্ষা সমস্যা নয়। পরিবর্তে, ইনিশিয়ালাইজারটি প্রথম চালায় (এটি কোডের আগের লাইনে রয়েছে, একটি নিম্ন রেখার সংখ্যা)। তারপরে ইনিশিয়ালাইজারটি চালিত হয়, যা উচ্চ-কেসটিকে সমান করে তোলে । এবং তারপরে "সুস্পষ্ট" স্ট্যাটিক কনস্ট্রাক্টর, টাইপ ইনিশিয়ালাইজার চালিত হয়, যা কেবল নিম্ন-কেস পরিবর্তন করে । সুতরাং এত কিছুর পরেও, পদ্ধতি (বা পদ্ধতি) এগিয়ে যেতে এবং আপার-কেস পড়তে পারে । এটির মান হ'ল এমনকি একটি মাত্র থ্রেড সহ। -1 x = -1X = GetX()X-1static C() { ... }xMainOtherX-1
জেপ্প স্টিগ নীলসেন

উত্তর:


189

স্ট্যাটিক কনস্ট্রাক্টররা কোনও ক্লাসের কোনও উদাহরণ তৈরি হওয়ার আগে বা কোনও স্থিতিশীল সদস্যদের অ্যাক্সেস পাওয়ার আগে অ্যাপ্লিকেশন ডোমেন প্রতি একবার একবার চালানোর গ্যারান্টিযুক্ত। https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors

প্রদর্শিত বাস্তবায়নটি প্রাথমিক নির্মাণের জন্য থ্রেড নিরাপদ, অর্থাত্ সিঙ্গেলটন অবজেক্ট তৈরির জন্য কোনও লকিং বা নাল পরীক্ষার প্রয়োজন নেই। তবে এর অর্থ এই নয় যে এই দৃষ্টান্তের কোনও ব্যবহার সিঙ্ক্রোনাইজ হবে। এটি বিভিন্ন উপায়ে করা যেতে পারে; আমি নীচে একটি দেখিয়েছি।

public class Singleton
{
    private static Singleton instance;
    // Added a static mutex for synchronising use of instance.
    private static System.Threading.Mutex mutex;
    private Singleton() { }
    static Singleton()
    {
        instance = new Singleton();
        mutex = new System.Threading.Mutex();
    }

    public static Singleton Acquire()
    {
        mutex.WaitOne();
        return instance;
    }

    // Each call to Acquire() requires a call to Release()
    public static void Release()
    {
        mutex.ReleaseMutex();
    }
}

53
মনে রাখবেন যে আপনার সিঙ্গলটন অবজেক্টটি যদি অপরিবর্তনীয় হয় তবে মিউটেক্স বা কোনও সিঙ্ক্রোনাইজেশন প্রক্রিয়া ব্যবহার করা ওভারকিল এবং ব্যবহার করা উচিত নয়। এছাড়াও, আমি উপরের নমুনা বাস্তবায়নটিকে অত্যন্ত নৃশংসভাবে খুঁজে পাই :-)। সিঙ্গেলটন.অ্যাকুইয়ার () ব্যবহার করে সমস্ত কোড সিঙ্গলটন উদাহরণ ব্যবহার করে সিঙ্গেলটন.রিলিজ () কল করার আশা করা হয়। এটি করতে ব্যর্থ (উদাহরণস্বরূপ অকাল ফিরে আসা, ব্যতিক্রমের মাধ্যমে সুযোগ ছেড়ে যাওয়া, রিলিজ কল করতে ভুলে), পরের বার যখন এই সিঙ্গলটনটি আলাদা থ্রেড থেকে অ্যাক্সেস করা হবে তখন এটি সিঙ্গলটন.অ্যাকুইয়ারে ডিডলক হবে ()।
মিলান গার্ডিয়ান

2
সম্মত, যদিও আমি আরও যেতে হবে। যদি আপনার সিঙ্গলটন অপরিবর্তনীয় হয় তবে সিঙ্গলটন ব্যবহার করা ওভারকিল। কেবল ধ্রুবককে সংজ্ঞায়িত করুন। শেষ পর্যন্ত, একটি সিঙ্গলটন সঠিকভাবে ব্যবহারের জন্য বিকাশকারীরা তারা কী করছে তা জানতে হবে। এই বাস্তবায়ন যতটা ভঙ্গুর, তবুও এই প্রশ্নগুলির তুলনায় এটির চেয়ে ভাল যেখানে এই ত্রুটিগুলি স্পষ্টতই অপ্রকাশিত মিউটেক্সের পরিবর্তে এলোমেলোভাবে প্রকাশ পায়।
জুবা

26
রিলিজ () পদ্ধতির ভঙ্গুরতা হ্রাস করার একটি উপায় হ'ল সিঙ্ক হ্যান্ডলার হিসাবে আইডিস্পোসেবল সহ অন্য শ্রেণি ব্যবহার করা। আপনি যখন সিঙ্গেলটনটি অর্জন করবেন, আপনি হ্যান্ডলারটি পাবেন এবং রিলিজটি হ্যান্ডেল করার জন্য সিঙ্গলটনের প্রয়োজনীয় কোডটি ব্যবহারের ব্লকটিতে রাখতে পারেন।
কোডেক্সআর্কানিয়াম

5
অন্যদের জন্য যারা এগুলি ছিন্ন করতে পারে: স্ট্যাটিক কনস্ট্রাক্টর বলার আগে ইনিশিয়ালাইজার সহ যে কোনও স্থির ক্ষেত্র সদস্যকে আরম্ভ করা হয় ।
অ্যাডাম ডব্লিউ। ম্যাককিনলে

12
এই দিনগুলির উত্তরটি ব্যবহার করতে হবে Lazy<T>- আমি যে কোডটি মূলত পোস্ট করেছি সে ব্যবহার করে যে সে ভুল করে চলেছে (এবং সত্যই যে এটি শুরু করা ভাল ছিল না - 5 বছর আগে-আমার বর্তমান হিসাবে এই স্টাফটিতে তেমন ভাল ছিল না -আমি :) ).
জুবা

86

যদিও এই সমস্ত উত্তর একই সাধারণ উত্তর দিচ্ছে, সেখানে একটি সতর্কতা রয়েছে।

মনে রাখবেন যে জেনেরিক শ্রেণির সমস্ত সম্ভাব্য ডেরাইভেশনগুলি পৃথক ধরণের হিসাবে সংকলিত হয়। জেনেরিক ধরণের জন্য স্থিতিশীল কন্সট্রাক্টরগুলি প্রয়োগ করার সময় সতর্কতা অবলম্বন করুন।

class MyObject<T>
{
    static MyObject() 
    {
       //this code will get executed for each T.
    }
}

সম্পাদনা করুন:

এখানে বিক্ষোভ প্রদর্শন:

static void Main(string[] args)
{
    var obj = new Foo<object>();
    var obj2 = new Foo<string>();
}

public class Foo<T>
{
    static Foo()
    {
         System.Diagnostics.Debug.WriteLine(String.Format("Hit {0}", typeof(T).ToString()));        
    }
}

কনসোলে:

Hit System.Object
Hit System.String

টাইপফ (মাইবজেক্ট <টি>)! = টাইপফ (মাইবজেক্ট <ওয়াই);
করিম আঘা

6
আমি মনে করি যে এটিই আমি তৈরি করার চেষ্টা করছি। জেনেরিক প্রকারগুলি পৃথক প্রকারের ভিত্তিতে জেনেরিক প্যারামিটারগুলি ব্যবহৃত হয় বলে সংকলিত হয়, সুতরাং স্থির নির্মাতা একাধিকবার কল করতে এবং ডাকতে পারে।
ব্রায়ান রুডল্ফ

1
এই অধিকার হয় যখন টি, মান ধরনের হয় রেফারেন্স টাইপ টি শুধুমাত্র একটি জেনেরিক টাইপ উত্পন্ন করা করবে
SLL

2
@ এসএলএল: সত্য নয় ... আমার সম্পাদনা দেখুন
ব্রায়ান রুডল্ফ

2
আকর্ষণীয় তবে সত্যই স্থির কাস্ট্রাক্টর সব ধরণের জন্য ডেকেছে, কেবল একাধিক রেফারেন্স প্রকারের জন্য চেষ্টা করেছে
sll করুন

28

একটি স্ট্যাটিক কন্সট্রাকটর আসলে ব্যবহার করা হয় threadsafe। স্ট্যাটিক কনস্ট্রাক্টর একবারেই মৃত্যুদন্ড কার্যকর করার গ্যারান্টিযুক্ত।

সি # ভাষার স্পেসিফিকেশন থেকে :

কোনও শ্রেণীর স্থিতিশীল নির্মাতা প্রদত্ত অ্যাপ্লিকেশন ডোমেনে একবারে কার্যকর করে। স্থিতিশীল কন্সট্রাক্টরের সম্পাদনটি একটি অ্যাপ্লিকেশন ডোমেনের মধ্যে ঘটে যাওয়া নিম্নলিখিত ইভেন্টগুলির প্রথম দ্বারা ট্রিগার করা হয়:

  • শ্রেণীর একটি উদাহরণ তৈরি করা হয়।
  • শ্রেণীর স্থির সদস্যগুলির মধ্যে যে কোনও রেফারেন্স করা হয়।

হ্যাঁ, আপনি বিশ্বাস করতে পারেন যে আপনার সিঙ্গলটন সঠিকভাবে ইনস্ট্যান্ট হবে।

চিড়িয়াখানা একটি দুর্দান্ত পয়েন্ট করেছে (এবং আমার 15 সেকেন্ড আগেও!) যে স্ট্যাটিক নির্মাণকারী এককটিতে থ্রেড-নিরাপদ ভাগ করে নেওয়ার গ্যারান্টি দেবে না। এটি অন্যভাবে পরিচালনা করা দরকার।


8

সি # সিঙ্গলটনের উপরের এমএসডিএন পৃষ্ঠা থেকে ক্লিফোটস সংস্করণটি এখানে রয়েছে:

নিম্নলিখিত ধরণটি ব্যবহার করুন, সর্বদা, আপনি ভুল হতে পারবেন না:

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

   public static Singleton Instance
   {
      get 
      {
         return instance; 
      }
   }
}

সুস্পষ্ট সিঙ্গলটন বৈশিষ্ট্যগুলি ছাড়িয়ে, এটি আপনাকে এই দুটি জিনিস বিনামূল্যে দেয় (সি ++ তে সিঙ্গেলনের ক্ষেত্রে):

  1. অলস নির্মাণ (বা এটি নির্মাণের আগে বলা না হলে কোনও নির্মাণ)
  2. সিঙ্ক্রোনাইজেশন

3
অলস যদি শ্রেণিতে অন্য কোনও সম্পর্কযুক্ত স্ট্যাটিক্স না থাকে (যেমন কনসেটস)। অন্যথায় কোনও স্থিতিশীল পদ্ধতি বা সম্পত্তি অ্যাক্সেস করার ফলে উদাহরণস্বরূপ সৃষ্টি হবে। তাই আমি এটিকে অলস বলব না।
Schultz9999

6

স্ট্যাটিক কনস্ট্রাক্টররা কেবলমাত্র অ্যাপ্লিকেশন ডোমেনের জন্য একবারে ফায়ার গ্যারান্টিযুক্ত যাতে আপনার পদ্ধতির ঠিক থাকে। তবে এটি কার্যকরীভাবে আরও সংক্ষিপ্ত, ইনলাইন সংস্করণ থেকে আলাদা নয়:

private static readonly Singleton instance = new Singleton();

আপনি যখন অলসভাবে জিনিস শুরু করতে চলেছেন তখন থ্রেড সুরক্ষা আরও একটি বিষয়।


4
অ্যান্ড্রু, এটি সম্পূর্ণ সমতুল্য নয়। স্ট্যাটিক কনস্ট্রাক্টর ব্যবহার না করে, কখন ইনিশিয়ালাইজার কার্যকর করা হবে সে সম্পর্কে কিছু গ্যারান্টি হারিয়ে গেছে। দয়া করে গভীর ব্যাখ্যা জন্য এই লিঙ্ক দেখুন: * < csharpindepth.com/Articles/General/Beforefieldinit.aspx > * < ondotnet.com/pub/a/dotnet/2003/07/07/staticxtor.html >
ডেরেক পার্ক

ডেরেক, আমি পূর্বের ক্ষেত্রের "অপ্টিমাইজেশন" এর সাথে পরিচিত তবে ব্যক্তিগতভাবে আমি এ নিয়ে কখনই চিন্তা করি না।
অ্যান্ড্রু পিটারস

@ ডেরেকপার্কের মন্তব্যের জন্য কাজ করার লিঙ্ক: csharpindepth.com / আর্টিকেলস / জেনারাল / বেফারফিল্যান্ডিনিত.এএসপিএক্স । এই লিঙ্কটি বাসি বলে মনে হচ্ছে: ondotnet.com/pub/a/dotnet/2003/07/07/staticxtor.html
ফগ 21

4

স্ট্যাটিক কনস্ট্রাক্টর কোনও থ্রেডকে ক্লাস অ্যাক্সেস করার অনুমতি দেওয়ার আগে চলমান শেষ করবে ।

    private class InitializerTest
    {
        static private int _x;
        static public string Status()
        {
            return "_x = " + _x;
        }
        static InitializerTest()
        {
            System.Diagnostics.Debug.WriteLine("InitializerTest() starting.");
            _x = 1;
            Thread.Sleep(3000);
            _x = 2;
            System.Diagnostics.Debug.WriteLine("InitializerTest() finished.");
        }
    }

    private void ClassInitializerInThread()
    {
        System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() starting.");
        string status = InitializerTest.Status();
        System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() status = " + status);
    }

    private void classInitializerButton_Click(object sender, EventArgs e)
    {
        new Thread(ClassInitializerInThread).Start();
        new Thread(ClassInitializerInThread).Start();
        new Thread(ClassInitializerInThread).Start();
    }

উপরের কোডটি নীচে ফলাফল তৈরি করেছে।

10: ClassInitializerInThread() starting.
11: ClassInitializerInThread() starting.
12: ClassInitializerInThread() starting.
InitializerTest() starting.
InitializerTest() finished.
11: ClassInitializerInThread() status = _x = 2
The thread 0x2650 has exited with code 0 (0x0).
10: ClassInitializerInThread() status = _x = 2
The thread 0x1f50 has exited with code 0 (0x0).
12: ClassInitializerInThread() status = _x = 2
The thread 0x73c has exited with code 0 (0x0).

স্থির নির্মাতা চালাতে দীর্ঘ সময় নিলেও, অন্যান্য থ্রেডগুলি বন্ধ হয়ে অপেক্ষা করেছিল। সমস্ত থ্রেড স্ট্যাটিক কনস্ট্রাক্টরের নীচে _x সেট এর মান পড়ে।


3

প্রচলিত ভাষা ইনফ্রাস্ট্রাকচার স্পেসিফিকেশন গ্যারান্টী যে, "একটি টাইপ সূচনাকারী, কোনো ধরনের জন্য ঠিক একবার চালানো হইবে, যদি না স্পষ্টভাবে ব্যবহারকারী কোড ডাকা।" (বিভাগ 9.5.3.1।) সুতরাং আপনার কাছে যদি শিথিল কলিং সিঙ্গলটনে :: কিছুটা অস্পষ্ট আইএল না থাকে তবে সরাসরি সিটিেক্টর (সম্ভাবনা নেই) সিঙ্গেলটন প্রকারটি ব্যবহারের আগে আপনার স্ট্যাটিক কনস্ট্রাক্টর ঠিক একবার চালানো হবে, কেবলমাত্র সিঙ্গেলনের একটি উদাহরণ তৈরি হবে, এবং আপনার দৃষ্টান্তের সম্পত্তিটি থ্রেড-নিরাপদ।

মনে রাখবেন যে সিঙ্গেলনের নির্মাতা যদি ইনস্ট্যান্ট সম্পত্তিটি (এমনকি অপ্রত্যক্ষভাবে) অ্যাক্সেস করে তবে ইনস্ট্যান্সের সম্পত্তিটি বাতিল হয়ে যাবে। আপনি যা করতে পারেন তা হ'ল এটি কখন ঘটে থাকে তা সনাক্ত করে এবং একটি ব্যতিক্রম ছুঁড়ে ফেলা হয়, এটি পরীক্ষা করে সম্পত্তি অ্যাক্সেসরে অকার্যকর। আপনার স্ট্যাটিক কনস্ট্রাক্টর ইনস্ট্যান্স সম্পত্তিটি সম্পূর্ণ করার পরে অকার্যকর হবে।

জুম্বার উত্তরটি নির্দেশ করে যেহেতু আপনাকে একাধিক থ্রেড থেকে অ্যাক্সেস করার জন্য সিঙ্গলটনকে নিরাপদ করতে হবে, বা সিঙ্গলটন উদাহরণটি ব্যবহার করে চারপাশে লকিং ব্যবস্থা প্রয়োগ করতে হবে।


2

কেবল পেডেন্টিক হতে, তবে স্ট্যাটিক কনস্ট্রাক্টর হিসাবে তেমন কোনও জিনিস নেই, বরং স্থির ধরণের ইনিশিয়ালাইজার, এখানে চক্রীয় স্ট্যাটিক কনস্ট্রাক্টর নির্ভরতার একটি ছোট ডেমো যা এই পয়েন্টটি চিত্রিত করে।


1
মাইক্রোসফ্ট একমত না বলে মনে হচ্ছে। msdn.microsoft.com/en-us/library/k9x6w0hc.aspx
Westy92


0

যদিও অন্যান্য উত্তরগুলি বেশিরভাগই সঠিক, স্থির নির্মাতাদের সাথে আরও একটি সতর্কতামূলক কাজ রয়েছে।

বিভাগ II.10.5.3.3 অনুসারে ইসিএমএ -৩৩5 সাধারণ ভাষা অবকাঠামোর রেস এবং অচলাবস্থা

প্রারম্ভিক একাকীকরণ একাকী কোনও অচলাবস্থা তৈরি করতে পারে না যদি না টাইপ ইনিশিয়ালাইজার (সরাসরি বা অপ্রত্যক্ষভাবে) থেকে কল করা কিছু কোড স্পষ্টভাবে ব্লকিং ক্রিয়াকলাপের আহ্বান করে।

নিম্নলিখিত কোডগুলির একটি অচলাবস্থার ফলাফল

using System.Threading;
class MyClass
{
    static void Main() { /* Won’t run... the static constructor deadlocks */  }

    static MyClass()
    {
        Thread thread = new Thread(arg => { });
        thread.Start();
        thread.Join();
    }
}

মূল লেখক হলেন ইগর ওস্ট্রভস্কি, তার পোস্টটি এখানে দেখুন

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.