লক (এটি) {…} খারাপ কেন?


484

MSDN ডকুমেন্টেশন বলছেন যে

public class SomeObject
{
  public void SomeOperation()
  {
    lock(this)
    {
      //Access instance variables
    }
  }
}

"সমস্যাটি যদি প্রকাশ্যে অ্যাক্সেস করা যায় তবে তা"। আমি ভাবছি কেন? লকটি প্রয়োজনীয়তার চেয়ে বেশি সময় ধরে রাখা হবে বলেই কি? নাকি এরপরে আরও কিছু কুখ্যাত কারণ রয়েছে?

উত্তর:


508

thisলক স্টেটমেন্টগুলিতে এটি ব্যবহার করা খারাপ ফর্ম কারণ এটি সাধারণত আপনার নিয়ন্ত্রণের বাইরে থাকে এবং অন্য কেউ সেই জিনিসটিতে লক করছেন।

সমান্তরাল ক্রিয়াকলাপের যথাযথ পরিকল্পনা করার জন্য, সম্ভাব্য অচলাবস্থার পরিস্থিতি বিবেচনা করার জন্য বিশেষভাবে যত্ন নেওয়া উচিত এবং অজানা সংখ্যক লক এন্ট্রি পয়েন্টগুলি এতে বাধা সৃষ্টি করে। উদাহরণস্বরূপ, কোনও অবজেক্টের রেফারেন্স সহ যে কেউ অবজেক্ট ডিজাইনার / স্রষ্টাকে সম্পর্কে জেনেও এটি লক করতে পারে। এটি বহু-থ্রেডযুক্ত সমাধানগুলির জটিলতা বৃদ্ধি করে এবং তাদের সঠিকতাটিকে প্রভাবিত করতে পারে।

একটি ব্যক্তিগত ক্ষেত্রটি সাধারণত আরও ভাল বিকল্প হয় কারণ সংকলক এতে অ্যাক্সেস সীমাবদ্ধতা প্রয়োগ করে এবং এটি লকিংয়ের ব্যবস্থাটি সজ্জিত করে। ব্যবহার thisজনসাধারণের জন্য আপনার লক বাস্তবায়নের অংশ প্রকাশক দ্বারা এনক্যাপস্যুলেশন লঙ্ঘন করে। এটি কোনও স্পষ্ট নয় যে thisডকুমেন্টটি না থাকলে আপনি কোনও লকটি অর্জন করবেন । তারপরেও কোনও সমস্যা রোধ করতে ডকুমেন্টেশনের উপর নির্ভর করা উপ-অনুকূল is

অবশেষে, এমন সাধারণ ভুল ধারণা রয়েছে যা lock(this)আসলে প্যারামিটার হিসাবে পাস করা বস্তুকে সংশোধন করে এবং কোনও উপায়ে এটি কেবল পঠনযোগ্য বা অ্যাক্সেসযোগ্য করে তোলে। এটা মিথ্যাlockনিছক একটি কী হিসাবে পরিবেশন করার জন্য পরামিতি হিসাবে অবজেক্টটি পাস করেছে । যদি ইতিমধ্যে সেই কীটিতে কোনও লক ধরে রাখা থাকে তবে লকটি তৈরি করা যায় না; অন্যথায়, লক অনুমোদিত।

এ কারণেই lockস্টেটমেন্টগুলিতে কীগুলি হিসাবে স্ট্রিংগুলি ব্যবহার করা খারাপ , কারণ সেগুলি পরিবর্তনযোগ্য এবং অ্যাপ্লিকেশনটির বিভিন্ন অংশে ভাগ / অ্যাক্সেসযোগ্য। পরিবর্তে আপনার একটি ব্যক্তিগত ভেরিয়েবল ব্যবহার করা উচিত, একটি Objectউদাহরণ সুন্দরভাবে করবে।

উদাহরণস্বরূপ নিম্নলিখিত সি # কোডটি চালান।

public class Person
{
    public int Age { get; set;  }
    public string Name { get; set; }

    public void LockThis()
    {
        lock (this)
        {
            System.Threading.Thread.Sleep(10000);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var nancy = new Person {Name = "Nancy Drew", Age = 15};
        var a = new Thread(nancy.LockThis);
        a.Start();
        var b = new Thread(Timewarp);
        b.Start(nancy);
        Thread.Sleep(10);
        var anotherNancy = new Person { Name = "Nancy Drew", Age = 50 };
        var c = new Thread(NameChange);
        c.Start(anotherNancy);
        a.Join();
        Console.ReadLine();
    }

    static void Timewarp(object subject)
    {
        var person = subject as Person;
        if (person == null) throw new ArgumentNullException("subject");
        // A lock does not make the object read-only.
        lock (person.Name)
        {
            while (person.Age <= 23)
            {
                // There will be a lock on 'person' due to the LockThis method running in another thread
                if (Monitor.TryEnter(person, 10) == false)
                {
                    Console.WriteLine("'this' person is locked!");
                }
                else Monitor.Exit(person);
                person.Age++;
                if(person.Age == 18)
                {
                    // Changing the 'person.Name' value doesn't change the lock...
                    person.Name = "Nancy Smith";
                }
                Console.WriteLine("{0} is {1} years old.", person.Name, person.Age);
            }
        }
    }

    static void NameChange(object subject)
    {
        var person = subject as Person;
        if (person == null) throw new ArgumentNullException("subject");
        // You should avoid locking on strings, since they are immutable.
        if (Monitor.TryEnter(person.Name, 30) == false)
        {
            Console.WriteLine("Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string \"Nancy Drew\".");
        }
        else Monitor.Exit(person.Name);

        if (Monitor.TryEnter("Nancy Drew", 30) == false)
        {
            Console.WriteLine("Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!");
        }
        else Monitor.Exit("Nancy Drew");
        if (Monitor.TryEnter(person.Name, 10000))
        {
            string oldName = person.Name;
            person.Name = "Nancy Callahan";
            Console.WriteLine("Name changed from '{0}' to '{1}'.", oldName, person.Name);
        }
        else Monitor.Exit(person.Name);
    }
}

কনসোল আউটপুট

'this' person is locked!
Nancy Drew is 16 years old.
'this' person is locked!
Nancy Drew is 17 years old.
Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string "Nancy Drew".
'this' person is locked!
Nancy Smith is 18 years old.
'this' person is locked!
Nancy Smith is 19 years old.
'this' person is locked!
Nancy Smith is 20 years old.
Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!
'this' person is locked!
Nancy Smith is 21 years old.
'this' person is locked!
Nancy Smith is 22 years old.
'this' person is locked!
Nancy Smith is 23 years old.
'this' person is locked!
Nancy Smith is 24 years old.
Name changed from 'Nancy Drew' to 'Nancy Callahan'.

2
আমি যেমন ছিটিয়েছি : (1) ন্যান্সি থ্রড 1 এ লকযুক্ত (এটি) রয়েছে। (2) একই ন্যান্সি থ্রেড 2 এজেডিং অবস্থায় রয়েছে যখন এখনও থ্রেড 1 এ লক রয়েছে - লক করা অবজেক্টটি কেবল পঠনযোগ্য নয়। ALSO (2a) থ্রেড 2 এ থাকাকালীন এই ন্যান্সি অবজেক্টটি নামের উপরও লক হয়ে আছে। (3) একই নামের সাথে একটি পৃথক বস্তু তৈরি করুন । (4) থ্রেড 3 এ প্রবেশ করুন এবং নাম দিয়ে লক করার চেষ্টা করুন। (বড় ফিনিস) তবে "স্ট্রিংগুলি অপরিবর্তনীয়" যার অর্থ কোনও পদার্থ "ন্যানসি ড্রু" উল্লেখ করে আক্ষরিকভাবে একই স্ট্রিং উদাহরণটিকে স্মৃতিতে দেখছে। সুতরাং অবজেক্ট 2 যখন স্ট্রিং-এ একই মানতে লক হয়ে থাকে তখন কোনও স্ট্রিংতে লক পাওয়া যায় না
রাডারবোব ২

পরিবর্তে স্ট্যান্ডার্ড ভেরিয়েবল ব্যবহার করা lock(this)হ'ল স্ট্যান্ডার্ড পরামর্শ; এটি লক্ষণীয় গুরুত্বপূর্ণ যে এটি করা সাধারণত বাইরের কোডের পক্ষে অ্যাক্সেসের সাথে যুক্ত লকটি পদ্ধতির কলগুলির মধ্যে ধরে রাখার কারণ হয়ে যায়। এটি ভাল জিনিস হতে পারে বা নাও হতে পারে । বাহ্যিক কোডটিকে স্বেচ্ছাসেবী সময়কালের জন্য একটি লক ধরে রাখার অনুমতি দেওয়ার ক্ষেত্রে কিছুটা বিপদ রয়েছে এবং ক্লাসগুলি সাধারণত এমনভাবে তৈরি করা উচিত যাতে এ জাতীয় ব্যবহার অপ্রয়োজনীয় হয়, তবে সবসময় ব্যবহারিক বিকল্প থাকে না। একটি সাধারণ উদাহরণ হিসাবে, যদি না কোনও সংগ্রহ তার নিজস্ব ToArrayবা ToListপদ্ধতি প্রয়োগ করে ...
সুপারক্যাট

4
(`IEnumerable <T> এক্সটেনশন পদ্ধতির বিপরীতে), থ্রেডের জন্য সংগ্রহের স্ন্যাপশটটি পেতে একটিই উপায় হ'ল সমস্ত পরিবর্তন লক করার সময় এটি গণনা করা যেতে পারে । এটি করার জন্য, এটি অবশ্যই কোনও লক অ্যাক্সেস করতে হবে যা কোনও কোড দ্বারা অর্জন করা হয়েছে যা সংগ্রহটি পরিবর্তন করবে। লকটি প্রকাশে ব্যর্থতা উদাহরণস্বরূপ প্রোগ্রামটিকে কালেকশনের একটি অ্যাসিনক্রোনাস স্ন্যাপশট সম্পাদন করা অসম্ভব করে তুলবে (যেমন একটি সংগ্রহ-ব্রাউজিং ইউজার ইন্টারফেস আপডেট করতে)।
সুপারক্যাট

there is the common misconception that lock(this) actually modifies the object passed as a parameter, and in some way makes it read-only or inaccessible. This is false- আমি বিশ্বাস করি যারা আলোচনা SyncBlock সম্পর্কে CLR বস্তু বিট তাই আনুষ্ঠানিকভাবে এই অধিকার হয় - লক পরিবর্তিত বস্তু নিজেই
SLL

@ ইস্তেবান, আমি আপনার উদাহরণটি সম্পূর্ণ পছন্দ করি, এটি দুর্দান্ত। তোমার কাছে আমার একটি প্রশ্ন আছে. নেম চেঞ্জ (..) পদ্ধতির কোডটি আপনার সাথে শেষ হয়: <কোড> যদি (মনিটর.ট্রিইন্টার (ব্যক্তি.নাম, 10000)) {{ । । Mon অন্যথায় নিরীক্ষণ করুন x এক্সিট (person.Name); </code> এটি দিয়ে শেষ হওয়া উচিত নয়: <কোড> যদি (মনিটর T ট্রাইএন্টার (ব্যক্তি। নাম, 10000)) {{ । । Monitor.Exit (person.Name); } </code>
আভিফারাহ

64

কারণ লোকেরা যদি আপনার অবজেক্টের উদাহরণে (যেমন: আপনার this) পয়েন্টারটি পেতে পারে তবে তারা সেই একই বস্তুকে লক করার চেষ্টা করতে পারে। এখন তারা সচেতন হতে পারে না যে আপনি thisঅভ্যন্তরীণভাবে লক করছেন তাই এটি সমস্যার কারণ হতে পারে (সম্ভবত একটি অচলাবস্থা)

এগুলি ছাড়াও এটি একটি খারাপ অভ্যাসও কারণ এটি "অত্যধিক" লক করছে

উদাহরণস্বরূপ, আপনার একটি সদস্য ভেরিয়েবল থাকতে পারে List<int>এবং কেবলমাত্র আপনাকে লক করা দরকার কেবল সেই সদস্যের ভেরিয়েবল। আপনি যদি নিজের ফাংশনগুলিতে পুরো অবজেক্টটি লক করেন তবে অন্য যে জিনিসগুলিতে এই ফাংশনগুলি বলা হয় তা লকের জন্য অপেক্ষা করে অবরুদ্ধ হয়ে যাবে। এই ফাংশনগুলির যদি সদস্য তালিকার অ্যাক্সেসের প্রয়োজন না হয়, আপনি অন্য কোডটি অপেক্ষা করতে এবং বিনা কারণে আপনার অ্যাপ্লিকেশনটি ধীর করে দেবেন।


44
এই উত্তরের শেষ অনুচ্ছেদটি সঠিক নয়। লক কোনওভাবেই অবজেক্টকে অ্যাক্সেসযোগ্য বা কেবল পঠনযোগ্য করে তোলে না। লক (এটি) এর দ্বারা রেফারেন্স করা অবজেক্টটিকে কল করা বা পরিবর্তন করতে অন্য থ্রেডকে বাধা দেয় না।
Esteban Brenes

3
এটি করা হয় যদি অন্য পদ্ধতিগুলি বলা হচ্ছে একটি লক (এটি) করে। আমি বিশ্বাস করি সেটাই তিনি তৈরি করছিলেন। "আপনি যদি আপনার ফাংশনগুলিতে পুরো বস্তুটি লক করেন তবে" লক্ষ্য করুন ...
হার্মস

@ অরিয়ন: এটি পরিষ্কার। @ হার্মস: হ্যাঁ, তবে সেই কার্যকারিতা অর্জনের জন্য আপনার 'এটি' ব্যবহার করার দরকার নেই, তালিকাগুলিতে সিঙ্করুট সম্পত্তি সেই উদ্দেশ্যে কাজ করে, উদাহরণস্বরূপ, এটি পরিষ্কার করার সময় সেই কীটিতে সিঙ্ক্রোনাইজেশন করা উচিত।
Esteban Brenes 21

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

শেষ অনুচ্ছেদটি এখনও বোঝা যায় না। আপনার যদি কেবল তালিকার অ্যাক্সেসকে সীমাবদ্ধ করার প্রয়োজন হয়, অন্য ফাংশনগুলির তালিকায় অ্যাক্সেস না করলে কেন লক থাকবে?
জোয়াকিম এমএইচ

44

এমএসডিএন টপিক থ্রেড সিঙ্ক্রোনাইজেশন (সি # প্রোগ্রামিং গাইড) একবার দেখুন

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


34

আমি জানি এটি একটি পুরানো থ্রেড, তবে যেহেতু লোকেরা এখনও এটি দেখতে এবং এটির উপর নির্ভর করতে পারে তাই উল্লেখ করা গুরুত্বপূর্ণ যে এটির lock(typeof(SomeObject))চেয়েও খারাপ lock(this)। বলেছে যে; অ্যালানকে আন্তরিক কুদোস দেখানোর জন্য lock(typeof(SomeObject))এটি খারাপ অনুশীলন।

এর উদাহরণ System.Typeহ'ল একটি সর্বাধিক জেনারিক, মোটা দানাদার জিনিস। খুব কমপক্ষে, System.Type এর একটি উদাহরণ একটি AppDomain এর কাছে বিশ্বব্যাপী, এবং .NET একটি অ্যাপডোমায়নে একাধিক প্রোগ্রাম চালাতে পারে। এর অর্থ হ'ল দুটি সম্পূর্ণ ভিন্ন প্রোগ্রাম সম্ভাব্যভাবে একটি অচলাবস্থা তৈরির পরিমাণ পর্যন্ত একে অপরের মধ্যে হস্তক্ষেপ সৃষ্টি করতে পারে যদি তারা উভয়ই একই ধরণের উদাহরণগুলিতে একটি সিঙ্ক্রোনাইজেশন লক পাওয়ার চেষ্টা করে।

সুতরাং lock(this)বিশেষত দৃust় আকার নয়, সমস্যা সৃষ্টি করতে পারে এবং উদ্ধৃত সমস্ত কারণে সর্বদা ভ্রু বাড়াতে হবে। তবুও বহুল ব্যবহৃত, তুলনামূলকভাবে সম্মানিত এবং আপাতদৃষ্টিতে স্থিতিশীল কোড যেমন লগ 4 নেট যা লক (এটি) প্যাটার্নটি ব্যাপকভাবে ব্যবহার করে, যদিও আমি ব্যক্তিগতভাবে সেই প্যাটার্নের পরিবর্তনটি দেখতে পছন্দ করি prefer

কিন্তু lock(typeof(SomeObject))কীটগুলির সম্পূর্ণ নতুন এবং বর্ধিত ক্যান খুলুন।

এর মূল্য কী।


26

... এবং ঠিক একই যুক্তিগুলি এই নির্মাণের ক্ষেত্রেও প্রযোজ্য:

lock(typeof(SomeObject))

17
লক (typeof (SomeObject)) আসলে লক (এই) (তুলনায় অনেক খারাপ stackoverflow.com/a/10510647/618649 )।
ক্রেগ

1
ঠিক আছে, লক (অ্যাপ্লিকেশন। বর্তমান) তখন আরও খারাপ, তবে যেকোনভাবে এই মূ ?় জিনিসগুলির মধ্যে কে চেষ্টা করবে? লক (এটি) যৌক্তিক এবং সাফল্য বলে মনে হচ্ছে, তবে এই অন্যান্য উদাহরণগুলি তা দেয় না।
জার শারদান

আমি সম্মত নই যে এটি lock(this)বিশেষত যৌক্তিক এবং সংহত বলে মনে হচ্ছে। এটি একটি মারাত্মক মোটা লক এবং অন্য কোনও কোড আপনার আইটেমটিতে একটি লক নিতে পারে, সম্ভাব্যভাবে আপনার অভ্যন্তরীণ কোডে হস্তক্ষেপ সৃষ্টি করে। আরও দানাদার লক নিন এবং আরও কঠোর নিয়ন্ত্রণ গ্রহণ করুন। কি lock(this)জন্য এটি যে এটি একটি অনেক বেশি ভালো যাচ্ছে আছে lock(typeof(SomeObject))
ক্রেগ

8

কল্পনা করুন যে আপনার অফিসে একজন দক্ষ সচিব আছেন যা বিভাগের একটি শেয়ার্ড রিসোর্স। কিছুক্ষণের মধ্যে, আপনি তাদের দিকে ছুটে আসেন কারণ আপনার একটি কাজ রয়েছে, কেবলমাত্র এই আশা করার জন্য আপনার সহকর্মীদের মধ্যে অন্য কেউ ইতিমধ্যে তাদের দাবি করেন নি। সাধারণত আপনাকে কেবল একটি স্বল্প সময়ের জন্য অপেক্ষা করতে হবে।

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

lock(this)আমরা দেখেছি হিসাবে খারাপ। বাইরের কোনও অবজেক্টটি সেই বস্তুকে লক করতে পারে এবং যেহেতু আপনি ক্লাসটি ব্যবহার করছেন তা আপনি নিয়ন্ত্রণ করেন না, তাই যে কেউ এটিকে লক করতে পারে ... যা উপরে বর্ণিত সঠিক উদাহরণ। আবার, সমাধানটি হ'ল অবজেক্টের এক্সপোজার সীমাবদ্ধ করা। তবে, আপনার যদি একটি private, protectedবা internalশ্রেণি থাকে তবে আপনি ইতিমধ্যে নিয়ন্ত্রণ করতে পারছেন কে আপনার বস্তুকে লক করছে , কারণ আপনি নিশ্চিত যে আপনি নিজের কোডটি লিখেছেন। সুতরাং এখানে বার্তাটি হ'ল: এটি প্রকাশ করবেন না public। এছাড়াও একইরকম দৃশ্যের জন্য কোনও লক ব্যবহৃত হয়েছে তা নিশ্চিত করে ডেডলকগুলি এড়ানো হয়।

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

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

একইভাবে, আপনি ডাব্লুসিএফ অবজেক্টস, এইচটিটিপি কনটেক্সট.কন্টেন্ট, থ্রেড.কন্টেন, সিঙ্গললেটস (সাধারণভাবে) ইত্যাদি লক করবেন না? এগুলি এড়ানো সবচেয়ে সহজ উপায়? private [static] object myLock = new object();


3
আসলে একটি প্রাইভেট ক্লাস থাকার বিষয়টি বাধা দেয় না। বাহ্যিক কোড একটি ব্যক্তিগত শ্রেণীর উদাহরণের জন্য একটি রেফারেন্স পেতে পারে ...
রাশাক

1
@ রাশাক আপনি প্রযুক্তিগতভাবে সঠিক হওয়ার সময় (এটি নির্দেশ করার জন্য +1), আমার বক্তব্যটি হ'ল উদাহরণটি কে লক করছে আপনার নিয়ন্ত্রণে থাকা উচিত। এর মতো দুরত্বগুলি ফিরে আসে।
আটলস্টে

4

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

আমি কী বলতে চাইছি তা দেখতে এই কোডটি দেখুন। কনসোল অ্যাপ্লিকেশনটিতে আপনার মূল প্রোগ্রামটিতে নিম্নলিখিত কোডটি যুক্ত করুন:

    static void Main(string[] args)
    {
         TestThreading();
         Console.ReadLine();
    }

    public static void TestThreading()
    {
        Random rand = new Random();
        Thread[] threads = new Thread[10];
        TestLock.balance = 100000;
        for (int i = 0; i < 10; i++)
        {
            TestLock tl = new TestLock();
            Thread t = new Thread(new ThreadStart(tl.WithdrawAmount));
            threads[i] = t;
        }
        for (int i = 0; i < 10; i++)
        {
            threads[i].Start();
        }
        Console.Read();
    }

নীচের মত একটি নতুন ক্লাস তৈরি করুন।

 class TestLock
{
    public static int balance { get; set; }
    public static readonly Object myLock = new Object();

    public void Withdraw(int amount)
    {
      // Try both locks to see what I mean
      //             lock (this)
       lock (myLock)
        {
            Random rand = new Random();
            if (balance >= amount)
            {
                Console.WriteLine("Balance before Withdrawal :  " + balance);
                Console.WriteLine("Withdraw        : -" + amount);
                balance = balance - amount;
                Console.WriteLine("Balance after Withdrawal  :  " + balance);
            }
            else
            {
                Console.WriteLine("Can't process your transaction, current balance is :  " + balance + " and you tried to withdraw " + amount);
            }
        }

    }
    public void WithdrawAmount()
    {
        Random rand = new Random();
        Withdraw(rand.Next(1, 100) * 100);
    }
}

এই লক করা প্রোগ্রাম একটি রান ।

   Balance before Withdrawal :  100000
    Withdraw        : -5600
    Balance after Withdrawal  :  94400
    Balance before Withdrawal :  100000
    Balance before Withdrawal :  100000
    Withdraw        : -5600
    Balance after Withdrawal  :  88800
    Withdraw        : -5600
    Balance after Withdrawal  :  83200
    Balance before Withdrawal :  83200
    Withdraw        : -9100
    Balance after Withdrawal  :  74100
    Balance before Withdrawal :  74100
    Withdraw        : -9100
    Balance before Withdrawal :  74100
    Withdraw        : -9100
    Balance after Withdrawal  :  55900
    Balance after Withdrawal  :  65000
    Balance before Withdrawal :  55900
    Withdraw        : -9100
    Balance after Withdrawal  :  46800
    Balance before Withdrawal :  46800
    Withdraw        : -2800
    Balance after Withdrawal  :  44000
    Balance before Withdrawal :  44000
    Withdraw        : -2800
    Balance after Withdrawal  :  41200
    Balance before Withdrawal :  44000
    Withdraw        : -2800
    Balance after Withdrawal  :  38400

মাই লকটিতে লক করা প্রোগ্রামটি এখানে রইল

Balance before Withdrawal :  100000
Withdraw        : -6600
Balance after Withdrawal  :  93400
Balance before Withdrawal :  93400
Withdraw        : -6600
Balance after Withdrawal  :  86800
Balance before Withdrawal :  86800
Withdraw        : -200
Balance after Withdrawal  :  86600
Balance before Withdrawal :  86600
Withdraw        : -8500
Balance after Withdrawal  :  78100
Balance before Withdrawal :  78100
Withdraw        : -8500
Balance after Withdrawal  :  69600
Balance before Withdrawal :  69600
Withdraw        : -8500
Balance after Withdrawal  :  61100
Balance before Withdrawal :  61100
Withdraw        : -2200
Balance after Withdrawal  :  58900
Balance before Withdrawal :  58900
Withdraw        : -2200
Balance after Withdrawal  :  56700
Balance before Withdrawal :  56700
Withdraw        : -2200
Balance after Withdrawal  :  54500
Balance before Withdrawal :  54500
Withdraw        : -500
Balance after Withdrawal  :  54000

1
আপনার উদাহরণে কোন জিনিসটি নোট করা উচিত তা যেমন আপনি কী দেখান যা ভুল। যখন আপনি Random rand = new Random();এনভিএম ব্যবহার করেন তখন কী ভুল তা স্পষ্ট করা আমার পক্ষে মনে হয় আমি এটির পুনরাবৃত্তি ব্যালেন্সটি দেখি
Seabizkit

3

মাইক্রোসফ্ট। নেট রানটাইমের জন্য পারফরম্যান্স আর্কিটেক্ট রিকো মারিয়ানির রচনা http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects সম্পর্কে এটি সম্পর্কে খুব ভাল নিবন্ধ রয়েছে

উদ্ধৃতাংশ:

এখানে মূল সমস্যাটি হ'ল আপনি টাইপ অবজেক্টের মালিক নন এবং আপনি জানেন না যে অন্য কেউ এটি অ্যাক্সেস করতে পারে। সাধারণভাবে, আপনি তৈরি করেননি এমন কোনও বস্তু লক করার উপর নির্ভর করা খুব খারাপ ধারণা এবং অন্য কারা অ্যাক্সেস করতে পারে তা জানেন না। এটি করা অচলাবস্থাকে আমন্ত্রণ জানায়। সবচেয়ে নিরাপদ উপায় হল কেবলমাত্র ব্যক্তিগত সামগ্রী লক করা।



2

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

class SomeClass
{
    public void SomeMethod(int id)
    {
        **lock(this)**
        {
            while(true)
            {
                Console.WriteLine("SomeClass.SomeMethod #" + id);
            }
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        SomeClass o = new SomeClass();

        lock(o)
        {
            for (int threadId = 0; threadId < 3; threadId++)
            {
                Thread t = new Thread(() => {
                    o.SomeMethod(threadId);
                        });
                t.Start();
            }

            Console.WriteLine();
        }

কাজ করার জন্য, এই লোকটি লকটির পরিবর্তে থ্রেড.ট্রাইমনিটর (টাইমআউট সহ) ব্যবহার করেছে:

            Monitor.TryEnter(temp, millisecondsTimeout, ref lockWasTaken);
            if (lockWasTaken)
            {
                doAction();
            }
            else
            {
                throw new Exception("Could not get lock");
            }

https://blogs.appbeat.io/post/c-how-to-lock-without-deadlocks


যতদূর আমি দেখতে পাচ্ছি, যখন আমি ব্যক্তিগত কোনও ব্যক্তিগত সদস্যের লকটির সাথে লকটি (এটি) প্রতিস্থাপন SomeClassকরি তখনও আমি একই অচলাবস্থা পাই। এছাড়াও, যদি প্রধান শ্রেণিতে লকটি প্রোগ্রামের অন্য কোনও বেসরকারী ইভেন্ট সদস্যের উপর করা হয়, তবে একই লকটি ঘটে occurs সুতরাং, নিশ্চিত না যে এই উত্তরটি বিভ্রান্তিকর এবং ভুল নয়। : এখানে যে আচরণ দেখুন dotnetfiddle.net/DMrU5h
Bartosz

1

কারণ আপনার শ্রেণীর উদাহরণ দেখতে পাবে এমন কোনও কোডের কোডটি সেই রেফারেন্সটিতে লকও করতে পারে। আপনি আপনার লকিং অবজেক্টটি আড়াল করতে (encapsulate) করতে চান যাতে কেবল কোডটির জন্য এটির রেফারেন্সের প্রয়োজন হয়। মূলশব্দটি এটি বর্তমান শ্রেণীর উদাহরণকে বোঝায়, সুতরাং যে কোনও সংখ্যক জিনিসের সাথে এটি উল্লেখ থাকতে পারে এবং থ্রেড সিঙ্ক্রোনাইজেশন করতে এটি ব্যবহার করতে পারে।

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


1

দুঃখিত, বন্ধুরা তবে আমি এই যুক্তিতে একমত হতে পারি না যে এটি লক করা অচলাবস্থার কারণ হতে পারে। আপনি দুটি জিনিসকে বিভ্রান্ত করছেন: অচেতন এবং অনাহার।

  • কোনও একটি থ্রেড বাধা না দিয়ে আপনি অচলাবস্থা বাতিল করতে পারবেন না তাই আপনার অচলাবস্থায় যাওয়ার পরে আপনি বেরিয়ে আসতে পারবেন না
  • থ্রেডগুলির একটির কাজ শেষ করার পরে অনাহার স্বয়ংক্রিয়ভাবে শেষ হবে

এখানে একটি ছবি যা পার্থক্য চিত্রিত করে।

উপসংহার থ্রেড অনাহার যদি আপনার সমস্যা না হয় তবে
আপনি এখনও নিরাপদে ব্যবহার করতে lock(this)পারেন। আপনার এখনও মনে রাখতে হবে যে যখন আপনার থ্রেডটি lock(this)আপনার বস্তুটি লক করে রাখা একটি লকটিতে শেষ হয়ে থ্রেড ক্ষুধার্ত হয়ে থাকে, তখন তা শেষ পর্যন্ত চিরন্তন অনাহারে শেষ হয়ে যাবে;)


9
একটি পার্থক্য আছে তবে এটি এই আলোচনার সাথে সম্পূর্ণ অপ্রাসঙ্গিক। এবং আপনার উপসংহারের প্রথম বাক্যটি ফ্ল্যাট ভুল।
বেন ভয়েগট

1
পরিষ্কার হতে হবে: আমি রক্ষা করছি না lock(this)- এই ধরণের কোডটি কেবল ভুল। আমি কেবল এটিকে ডেডলকিং বলা কিছুটা আপত্তিজনক বলে মনে করি।
SOReader

2
চিত্রের লিঙ্কটি আর উপলব্ধ নেই। :( যে কোনও সুযোগ আপনি এটিকে পুনরায় উল্লেখ করতে পারেন?
থেক্স

1

লক (এটি) ভাল ধারণা নয় কেন তা দয়া করে নীচের লিঙ্কটি উল্লেখ করুন।

http://blogs.msdn.com/b/bclteam/archive/2004/01/20/60719.aspx

সুতরাং সমাধানটি হল একটি ব্যক্তিগত অবজেক্ট যুক্ত করা, উদাহরণস্বরূপ, ক্লাসে লকঅবজেক্ট এবং কোড অঞ্চলটি লক স্টেটমেন্টের নীচে প্রদর্শিত হিসাবে রাখুন:

lock (lockObject)
{
...
}

1

এখানে কিছু নমুনা কোড অনুসরণ করা সহজ যা (আইএমও): ( লিনকপ্যাডে কাজ করবে , নিম্নলিখিত নেমস্পেসগুলিতে রেফারেন্স: সিস্টেম. নেট এবং সিস্টেম। ট্র্যাডিং.টাস্কস)

কিছু মনে রাখার বিষয় হ'ল লক (এক্স) মূলত সিনট্যাকটিক চিনি এবং এটি যা করে তা মনিটর.অন্টার ব্যবহার করে এবং তারপরে একটি চেষ্টা, ধরা, অবশেষে মনিটরের কল করতে ব্লক ব্যবহার করে xএক্সিট ব্যবহার করে। দেখুন: https://docs.microsoft.com/en-us/dotnet/api/system.threading.monitor.enter (মন্তব্য বিভাগ)

অথবা সি # লক স্টেটমেন্ট (ভিজ্যুয়াল বেসিকের সিঙ্কলক স্টেটমেন্ট) ব্যবহার করুন, যা এন্টার এবং প্রস্থান পদ্ধতিগুলিকে একটি পরীক্ষায় আবদ্ধ করে ... অবশেষে অবরুদ্ধ করুন।

void Main()
{
    //demonstrates why locking on THIS is BADD! (you should never lock on something that is publicly accessible)
    ClassTest test = new ClassTest();
    lock(test) //locking on the instance of ClassTest
    {
        Console.WriteLine($"CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        Parallel.Invoke(new Action[]
        {
            () => {
                //this is there to just use up the current main thread. 
                Console.WriteLine($"CurrentThread {Thread.CurrentThread.ManagedThreadId}");
                },
            //none of these will enter the lock section.
            () => test.DoWorkUsingThisLock(1),//this will dead lock as lock(x) uses Monitor.Enter
            () => test.DoWorkUsingMonitor(2), //this will not dead lock as it uses Montory.TryEnter
        });
    }
}

public class ClassTest
{
    public void DoWorkUsingThisLock(int i)
    {
        Console.WriteLine($"Start ClassTest.DoWorkUsingThisLock {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        lock(this) //this can be bad if someone has locked on this already, as it will cause it to be deadlocked!
        {
            Console.WriteLine($"Running: ClassTest.DoWorkUsingThisLock {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(1000);
        }
        Console.WriteLine($"End ClassTest.DoWorkUsingThisLock Done {i}  CurrentThread {Thread.CurrentThread.ManagedThreadId}");
    }

    public void DoWorkUsingMonitor(int i)
    {
        Console.WriteLine($"Start ClassTest.DoWorkUsingMonitor {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        if (Monitor.TryEnter(this))
        {
            Console.WriteLine($"Running: ClassTest.DoWorkUsingMonitor {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(1000);
            Monitor.Exit(this);
        }
        else
        {
            Console.WriteLine($"Skipped lock section!  {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        }

        Console.WriteLine($"End ClassTest.DoWorkUsingMonitor Done {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        Console.WriteLine();
    }
}

আউটপুট

CurrentThread 15
CurrentThread 15
Start ClassTest.DoWorkUsingMonitor 2 CurrentThread 13
Start ClassTest.DoWorkUsingThisLock 1 CurrentThread 12
Skipped lock section!  2 CurrentThread 13
End ClassTest.DoWorkUsingMonitor Done 2 CurrentThread 13

লক্ষ্য করুন যে থ্রেড # 12 এর মৃত লক হওয়ার পরে কখনও শেষ হয় না।


1
দ্বিতীয় DoWorkUsingThisLockথ্রেডটি ইস্যুটি বর্ণনা করার জন্য প্রয়োজনীয় নয় বলে মনে হচ্ছে ?
জ্যাক লু

আপনি কী বোঝাতে চাইছেন না প্রধান আউটটার লক, একটি থ্রেড কেবল অপরটির অপরটি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করবে? যা তখন সমান্তরালটিকে অকার্যকর করে দেবে ... আমি অনুভব করি আমাদের আরও ভাল বাস্তব বিশ্বের উদাহরণ প্রয়োজন ...
Seabizkit

@ শেবিজকিট, কোডটি আরও পরিষ্কার করার জন্য আপডেট করেছেন। সমান্তরালটি কেবল একটি নতুন থ্রেড তৈরি করতে এবং কোডটিকে অবিচ্ছিন্নভাবে চালানোর জন্য রয়েছে। বাস্তবে, ২ য় থ্রেডটি বিভিন্ন উপায়ে (বোতাম ক্লিক, পৃথক অনুরোধ, ইত্যাদি) দ্বারা আহ্বান করা যেতে পারে।
রাজ রাও ১৯

0

আপনি একটি নিয়ম স্থাপন করতে পারেন যা বলে যে একটি শ্রেণিতে এমন কোড থাকতে পারে যা 'এটি' বা লক করে এমন কোনও অবজেক্টকে ক্লাসের কোডটি ইনস্ট্যান্ট করে দেয়। সুতরাং যদি প্যাটার্নটি অনুসরণ না করা হয় তবে এটি কেবলমাত্র একটি সমস্যা।

আপনি যদি এমন কোড থেকে নিজেকে রক্ষা করতে চান যা এই প্যাটার্নটি অনুসরণ করে না, তবে গৃহীত উত্তরটি সঠিক। তবে যদি প্যাটার্নটি অনুসরণ করা হয় তবে এটি কোনও সমস্যা নয়।

লকের সুবিধা (এটি) দক্ষতা। আপনার যদি একটি সাধারণ "মান অবজেক্ট" থাকে যা একক মান ধারণ করে। এটি কেবল একটি মোড়ক, এবং এটি লক্ষ লক্ষ বার তাত্ক্ষণিকভাবে আসে। কেবল লক করার জন্য একটি বেসরকারী সিঙ্ক অবজেক্ট তৈরির প্রয়োজনীয়তার দ্বারা আপনি মূলত অবজেক্টটির আকার দ্বিগুণ করেছেন এবং বরাদ্দের সংখ্যা দ্বিগুণ করেছেন। যখন পারফরম্যান্স গুরুত্বপূর্ণ, এটি একটি সুবিধা।

আপনি যখন বরাদ্দ বা মেমরির পদক্ষেপের সংখ্যা সম্পর্কে চিন্তা করেন না, অন্যান্য উত্তরে নির্দেশিত কারণে লক (এটি) এড়ানো পছন্দনীয়।


-1

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


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