। নেট 4.0 এ কোন সমকালীন তালিকা নেই?


198

System.Collections.Concurrentনেট নেট in.০ তে নতুন নেমস্পেসটি দেখে আমি খুব শিহরিত হয়েছি ! আমি দেখেছি ConcurrentDictionary, ConcurrentQueue, ConcurrentStack, ConcurrentBagএবং BlockingCollection

রহস্যজনকভাবে মনে হচ্ছে এমন একটি জিনিস হ'ল ক ConcurrentList<T>। আমাকে কী লিখতে হবে (বা ওয়েব থেকে নামা :))?

আমি কি এখানে স্পষ্ট কিছু মিস করছি?


7
কনক্র্যান্টব্যাগ <টি> ( এমএসডিএন.ইমক্রোসফটকম / pt-br / library/… )
রদ্রিগো রেইস

4
@ রডরিগোরিস, কনক্র্যান্টব্যাগ <টি> একটি আনর্ডারড সংগ্রহ, যখন তালিকা <টি> অর্ডার করা হয়েছে।
অ্যাডাম কালভেট বোহল

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

পরিবর্তে একটি লক ব্যবহার করুন
এরিক বার্গস্টেট

ডটনেট উত্স কোডে থ্রেডসএফএললিস্ট.কস নামে একটি ফাইল রয়েছে যা নীচে কিছু কোডের মতো দেখতে দেখতে অনেকগুলি দেখাচ্ছে। এটি রিডার রাইটারলকস্লিম ব্যবহার করে এবং এটি নির্ধারণের চেষ্টা করছিল যে সরল লক (অবজেক্ট) এর পরিবর্তে কেন এটি ব্যবহার করবেন?
কলিন লামারে

উত্তর:


166

আমি কিছুক্ষণ আগে এটিকে চেষ্টা করে দেখলাম (এছাড়াও: গিটহাবটিতে )। আমার বাস্তবায়নে কিছু সমস্যা ছিল, যা আমি এখানে প্রবেশ করব না। আমি আপনাকে বলতে দিন, আরও গুরুত্বপূর্ণ, আমি কি শিখেছি।

প্রথমত, এর কোনও সম্পূর্ণ প্রয়োগ আপনি IList<T>আনার কোনও উপায় নেই তা লকহীন এবং থ্রেড-নিরাপদ। বিশেষত, এলোমেলোভাবে সন্নিবেশ এবং অপসারণগুলি কার্যকর হবে না , যদি না আপনি ও (1) এলোমেলো অ্যাক্সেস সম্পর্কে ভুলে যান (যেমন আপনি "প্রতারণা" না করে এবং কিছু সংযুক্ত তালিকার ব্যবহার না করে এবং সূচকটিকে চুষতে না দেন))

আমি কি চিন্তা উপযুক্ত হতে পারে একটি থ্রেড-নিরাপদ, সীমিত উপসেট ছিল IList<T>: বিশেষ করে, যেটা একটি সম্ভব হবে Addএবং র্যান্ডম প্রদান শুধুমাত্র পাঠযোগ্য সূচক মাধ্যমে অ্যাক্সেসের (কিন্তু কোন Insert, RemoveAtইত্যাদি, এবং এছাড়াও কোন র্যান্ডম লেখার এক্সেস)।

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

অপেক্ষাকৃত বিরল ইভেন্টের মধ্যে যে তালিকার অভ্যন্তরীণ অ্যারেটিকে নিজের আকার পরিবর্তন করতে হবে, আপনি একটি সামান্য ব্যয় করতে হবে। সুতরাং পরিশেষে আমি এই সিদ্ধান্তে পৌঁছেছি যে এটি একটি কুলুঙ্গিত দৃশ্যের যেখানে কোনও অ্যাড-ওনল ConcurrentList<T>সংগ্রহের ধরণটি বোঝায়: আপনি যখন প্রতিটি একক কলটিতে কোনও উপাদান যুক্ত করার গ্যারান্টিযুক্ত কম ওভারহেড চান (সুতরাং, একটি পরিমিত কর্মক্ষমতা লক্ষ্যের বিপরীতে)।

এটি কেবল আপনি যেমন ভাবেন ঠিক ততটা কার্যকর নয়।


52
এবং যদি আপনি অনুরূপ কিছু প্রয়োজন List<T>যে ব্যবহারসমূহ পুরোনো Skool, মনিটর ভিত্তিক সিঙ্ক্রোনাইজেশন সেখানে কারো SynchronizedCollection<T>: দূরে ছাত্রলীগের লুকানো msdn.microsoft.com/en-us/library/ms668265.aspx
LukeH

8
একটি ছোট সংযোজন: পুনরায় আকার দেওয়ার দৃশ্যটি এড়াতে (যতটা সম্ভব) ক্যাপাসিটি কনস্ট্রাক্টর প্যারামিটার ব্যবহার করুন।
হেন্ক হলটারম্যান

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

2
@ কেভিন: এটি এমন ConcurrentList<T>একটি ফ্যাশনে এমন নির্মাণ করা বেশ তুচ্ছ যে পাঠকরা তুলনামূলকভাবে সামান্য সংযোজন ওভারহেড সহ কোনও লকিংয়ের প্রয়োজন ছাড়াই ধারাবাহিক অবস্থা দেখার গ্যারান্টিযুক্ত। তালিকাটি উদাহরণস্বরূপ আকার 32 থেকে 64 পর্যন্ত প্রসারিত হলে আকার -32 অ্যারে রাখুন এবং একটি নতুন আকার -64 অ্যারে তৈরি করুন। পরবর্তী 32 টি আইটেমের প্রত্যেকটি যুক্ত করার সময়, এটি নতুন অ্যারের 32-63 স্লটে স্লটে রাখুন এবং একটি পুরানো আইটেমটি আকার -32 অ্যারে থেকে নতুনটিতে অনুলিপি করুন। Th৪ তম আইটেমটি যুক্ত না হওয়া পর্যন্ত পাঠকরা 0-31 আইটেমগুলির জন্য আকার -32 অ্যারে এবং 32-63 আইটেমগুলির জন্য আকার -32 অ্যারে দেখতে পাবেন।
সুপারক্যাট

2
Th৪ তম আইটেমটি যুক্ত হয়ে গেলে, সাইজ -32 অ্যারে এখনও 0-31 আইটেম আনার জন্য কাজ করবে তবে পাঠকদের আর এটি ব্যবহারের প্রয়োজন হবে না। তারা সমস্ত আইটেমের জন্য আকার -64 অ্যারে 0-63 এবং আইটেমের জন্য একটি আকার -128 অ্যারে 64-127 ব্যবহার করতে পারে। দুটি অ্যারেগুলির মধ্যে কোনটি ব্যবহার করবেন তা নির্বাচন করার ওভারহেড, এছাড়াও যদি প্রয়োজন হয় একটি মেমরি বাধা, এমনকি সবচেয়ে দক্ষ পাঠক-লেখক লক কল্পনাওযোগ্যর চেয়ে বেশি হবে। লেখকদের সম্ভবত লক ব্যবহার করা উচিত (লক-ফ্রি সম্ভব হবে, বিশেষত যদি প্রতিটি সন্নিবেশ সহ কোনও নতুন অবজেক্ট উদাহরণ তৈরি করা মনে না করে তবে লকটি সস্তা হওয়া উচিত।
সুপারক্যাট

38

আপনি কিসের জন্য একটি সমকালীন তালিকা ব্যবহার করবেন?

থ্রেডেড ওয়ার্ল্ডে এলোমেলো অ্যাক্সেস ধারক ধারণাটি কার্যকর হিসাবে এটি প্রদর্শিত হতে পারে না। বিবৃতি

  if (i < MyConcurrentList.Count)  
      x = MyConcurrentList[i]; 

সামগ্রিকভাবে এখনও থ্রেড-নিরাপদ হবে না।

কনক্র্যান্টলিস্ট তৈরির পরিবর্তে, যা আছে তা দিয়ে সমাধান তৈরি করার চেষ্টা করুন। সর্বাধিক সাধারণ ক্লাসগুলি হ'ল কনক্রন্টব্যাগ এবং বিশেষত ব্লকিংক্লেকশন।


ভাল যুক্তি. তবুও আমি যা করছি তা আরও কিছুটা জাগতিক। আমি কেবল কনকন্টারব্যাগ <টি> একটি আইলিস্ট <টি> এ নিয়োগের চেষ্টা করছি। আমি আমার সম্পত্তি একটি আইনিম্যুয়াল <টি> এ স্যুইচ করতে পারতাম, তবে আমি পারব না it এটিতে স্টাফ যুক্ত করুন।
অ্যালান

1
@ অ্যালান: তালিকাটি তালাবদ্ধ না করে এটি বাস্তবায়নের কোনও উপায় নেই। যেহেতু আপনি Monitorযেভাবেই এটি করতে ইতিমধ্যে ব্যবহার করতে পারেন তাই সমকালীন তালিকার কোনও কারণ নেই।
বিলি ওনিল

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

3
@ ডিসিপি - কনটেনসকি নিজেই থ্রেডসেফ, আপনার উদাহরণ (কন্টেনসকি নয়!) কেবল একটি রেসের শর্ত রয়েছে কারণ আপনি প্রথম সিদ্ধান্তের উপর নির্ভর করে দ্বিতীয় কল করেন যা এই সময়ে ইতিমধ্যে তারিখের বাইরে চলে যেতে পারে।
জারাত

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

19

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

এবং এর মূল্যের জন্য: আমি নিয়মিত তালিকা এবং সমকালীন তালিকায় 10,000,000 আইটেম যুক্ত করার একটি প্রাথমিক পরীক্ষা করেছি এবং ফলাফলগুলি ছিল:

তালিকাটি সমাপ্ত: 7793 মিলিসেকেন্ডে। একত্রে সমাপ্ত: 8064 মিলিসেকেন্ডে।

public class ConcurrentList<T> : IList<T>, IDisposable
{
    #region Fields
    private readonly List<T> _list;
    private readonly ReaderWriterLockSlim _lock;
    #endregion

    #region Constructors
    public ConcurrentList()
    {
        this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        this._list = new List<T>();
    }

    public ConcurrentList(int capacity)
    {
        this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        this._list = new List<T>(capacity);
    }

    public ConcurrentList(IEnumerable<T> items)
    {
        this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        this._list = new List<T>(items);
    }
    #endregion

    #region Methods
    public void Add(T item)
    {
        try
        {
            this._lock.EnterWriteLock();
            this._list.Add(item);
        }
        finally
        {
            this._lock.ExitWriteLock();
        }
    }

    public void Insert(int index, T item)
    {
        try
        {
            this._lock.EnterWriteLock();
            this._list.Insert(index, item);
        }
        finally
        {
            this._lock.ExitWriteLock();
        }
    }

    public bool Remove(T item)
    {
        try
        {
            this._lock.EnterWriteLock();
            return this._list.Remove(item);
        }
        finally
        {
            this._lock.ExitWriteLock();
        }
    }

    public void RemoveAt(int index)
    {
        try
        {
            this._lock.EnterWriteLock();
            this._list.RemoveAt(index);
        }
        finally
        {
            this._lock.ExitWriteLock();
        }
    }

    public int IndexOf(T item)
    {
        try
        {
            this._lock.EnterReadLock();
            return this._list.IndexOf(item);
        }
        finally
        {
            this._lock.ExitReadLock();
        }
    }

    public void Clear()
    {
        try
        {
            this._lock.EnterWriteLock();
            this._list.Clear();
        }
        finally
        {
            this._lock.ExitWriteLock();
        }
    }

    public bool Contains(T item)
    {
        try
        {
            this._lock.EnterReadLock();
            return this._list.Contains(item);
        }
        finally
        {
            this._lock.ExitReadLock();
        }
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        try
        {
            this._lock.EnterReadLock();
            this._list.CopyTo(array, arrayIndex);
        }
        finally
        {
            this._lock.ExitReadLock();
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new ConcurrentEnumerator<T>(this._list, this._lock);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return new ConcurrentEnumerator<T>(this._list, this._lock);
    }

    ~ConcurrentList()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
            GC.SuppressFinalize(this);

        this._lock.Dispose();
    }
    #endregion

    #region Properties
    public T this[int index]
    {
        get
        {
            try
            {
                this._lock.EnterReadLock();
                return this._list[index];
            }
            finally
            {
                this._lock.ExitReadLock();
            }
        }
        set
        {
            try
            {
                this._lock.EnterWriteLock();
                this._list[index] = value;
            }
            finally
            {
                this._lock.ExitWriteLock();
            }
        }
    }

    public int Count
    {
        get
        {
            try
            {
                this._lock.EnterReadLock();
                return this._list.Count;
            }
            finally
            {
                this._lock.ExitReadLock();
            }
        }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }
    #endregion
}

    public class ConcurrentEnumerator<T> : IEnumerator<T>
{
    #region Fields
    private readonly IEnumerator<T> _inner;
    private readonly ReaderWriterLockSlim _lock;
    #endregion

    #region Constructor
    public ConcurrentEnumerator(IEnumerable<T> inner, ReaderWriterLockSlim @lock)
    {
        this._lock = @lock;
        this._lock.EnterReadLock();
        this._inner = inner.GetEnumerator();
    }
    #endregion

    #region Methods
    public bool MoveNext()
    {
        return _inner.MoveNext();
    }

    public void Reset()
    {
        _inner.Reset();
    }

    public void Dispose()
    {
        this._lock.ExitReadLock();
    }
    #endregion

    #region Properties
    public T Current
    {
        get { return _inner.Current; }
    }

    object IEnumerator.Current
    {
        get { return _inner.Current; }
    }
    #endregion
}

5
ঠিক আছে, পুরাতন উত্তর কিন্তু এখনও: RemoveAt(int index)থ্রেড-নিরাপদ না হয়, Insert(int index, T item)== সূচির জন্য 0 শুধুমাত্র নিরাপদ, ফেরত IndexOf()অবিলম্বে ইত্যাদি পুরানো এমনকি সম্পর্কে শুরু করবেন না this[int]
হেন্ক হলটারম্যান

2
এবং আপনার দরকার নেই এবং কোনও ফাইনালাইজার () চান না want
হেন্ক হলটারম্যান

2
আপনি বলছেন যে অচলাবস্থার সম্ভাবনা রোধ করতে আপনি হাল ছেড়ে দিয়েছেন - এবং একসাথে একযোগে ব্যবহার করে সহজেইReaderWriterLockSlim অচলাবস্থা তৈরি করা যায় । তবে আপনি এটি ব্যবহার করবেন না, আপনি লকটি বাইরের অ্যাক্সেসযোগ্য করে তুলবেন না এবং আপনি যেমন কোনও পদ্ধতিতে কল করেন না যা কোনও রাইড লক ধরে রাখার সময় লেখার তালিকায় প্রবেশ করে, তাই আপনার ক্লাসটি ব্যবহার করে আর অচলাবস্থা তৈরি করে না সম্ভবত। EnterUpgradeableReadLock()
ইউজিন বেরেসভস্কি

1
সমবর্তী অ্যাক্সেসের জন্য অ-সমবর্তী ইন্টারফেসটি উপযুক্ত নয়। যেমন নিচেরটি পারমাণবিক নয় var l = new ConcurrentList<string>(); /* ... */ l[0] += "asdf";। সাধারণভাবে, একই সাথে কাজ করার পরে যে কোনও পঠন-লিখনের কম্বো আপনাকে গভীর সমস্যার মধ্যে নিয়ে যেতে পারে। সেজন্য সমবর্তী ডাটা স্ট্রাকচার সাধারণত তাদের জন্য পদ্ধতি, মত প্রদান ConcurrentDictionaryএর AddOrGetইত্যাদি দ্রষ্টব্য তোমার ধ্রুবক (এবং অপ্রয়োজনীয় কারণ সদস্যদের ইতিমধ্যে আন্ডারস্কোর দ্বারা এই ধরনের হিসাবে চিহ্নিত করা হয়েছে) এর পুনরাবৃত্তি this.clutters।
ইউজিন বেরেসভস্কি

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

11

ConcurrentList(একটি আকার পরিবর্তনযোগ্য অ্যারে হিসাবে, কোনও লিঙ্কযুক্ত তালিকা নয়) নন-ব্লকিং ক্রিয়াকলাপগুলি দিয়ে লেখা সহজ নয়। এর এপিআই একটি "সমবর্তী" সংস্করণে ভাল অনুবাদ করে না।


12
এটি কেবল লিখতে অসুবিধা নয়, একটি কার্যকর ইন্টারফেসটি খুঁজে পাওয়া এমনকি কঠিন।
কোডসইনচওস

11

কোন সমকালীন তালিকা নেই তার কারণ এটি মৌলিকভাবে লেখা যায় না। কারণ হ'ল আইলিস্টে বেশ কয়েকটি গুরুত্বপূর্ণ ক্রিয়াকলাপ সূচকগুলির উপর নির্ভর করে এবং কেবল প্লেইন কাজ করবে না। উদাহরণ স্বরূপ:

int catIndex = list.IndexOf("cat");
list.Insert(catIndex, "dog");

লেখক যে প্রভাব পরে চলেছেন তা হ'ল "কুকুর" "বিড়াল" এর আগে sertোকানো, তবে বহুবিবাহিত পরিবেশে কোডের এই দুটি লাইনের মধ্যে তালিকার মধ্যে যে কোনও কিছুই ঘটতে পারে। উদাহরণস্বরূপ, অন্য থ্রেডটি করতে পারে list.RemoveAt(0), পুরো তালিকাটি বামে স্থানান্তরিত করতে পারে, তবে গুরুত্বপূর্ণভাবে, ক্যাট ইন্ডেক্স পরিবর্তন হবে না। এখানে প্রভাবটি হ'ল Insertঅপারেশনটি আসলে "কুকুর "টিকে বিড়ালের পরে রাখবে, তার আগে নয়।

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

আপনি যদি মনে করেন আপনার একসাথে তালিকার দরকার, তবে সত্যিই কেবল দুটি সম্ভাবনা রয়েছে:

  1. আপনার সত্যিকারের যা দরকার তা হ'ল একটি কনক্র্যান্টব্যাগ
  2. আপনার নিজের সংগ্রহ তৈরি করতে হবে, সম্ভবত একটি তালিকা এবং আপনার নিজের সমঝোতা নিয়ন্ত্রণের সাথে প্রয়োগ করা হয়েছে।

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


5

যেসব ক্ষেত্রে পাঠ্য প্রচুর পরিমাণে বেশি পড়ে, বা (তবে ঘন ঘন) লেখাগুলি অ-সমবর্তী হয় , সেখানে অনুলিখনের অনুলিপিটি উপযুক্ত হতে পারে।

নীচে প্রদর্শিত বাস্তবায়ন হয়

  • lockless
  • একত্রে সাম্প্রতিক পরিবর্তনগুলি চলমান থাকা সত্ত্বেও সমকালীন পাঠগুলির জন্য নির্লজ্জভাবে দ্রুত - তারা যতক্ষণ সময় নেয় না কেন
  • কারণ "স্ন্যাপশট" অপরিবর্তনীয়, লকলেস পারমাণবিকতা সম্ভব, অর্থাত্ var snap = _list; snap[snap.Count - 1];কখনই হবে না (ভাল, অবশ্যই একটি খালি তালিকা বাদে) নিক্ষেপ করবে এবং আপনি স্ন্যাপশট সিনটিক্সের সাথে বিনামূল্যে থ্রেড-নিরাপদ গণনাও পাবেন .. আমি কীভাবে অপরিবর্তনীয়তা ভালবাসি!
  • জেনেরিক বাস্তবায়িত করার জন্য প্রযোজ্য কোনো ডাটা স্ট্রাকচার এবং পরিমার্জন কোন ধরনের
  • মৃত সরল , অর্থাত্ পরীক্ষা করে নেওয়া সহজ, ডিবাগ করা, কোডটি পড়ে যাচাই করা
  • নেট নেট 3.5 এ ব্যবহারযোগ্য

কাজ করার জন্য অনুলিপি করার জন্য, আপনাকে আপনার ডেটা কাঠামো কার্যকরভাবে অপরিবর্তনীয় রাখতে হবে , অর্থাৎ অন্য থ্রেডগুলিতে উপলব্ধ করার পরে কাউকে এগুলি পরিবর্তন করার অনুমতি নেই। আপনি যখন পরিবর্তন করতে চান, আপনি

  1. কাঠামো ক্লোন করুন
  2. ক্লোন পরিবর্তন করুন
  3. পরিবর্তিত ক্লোনটির রেফারেন্সে পরমাণুগতভাবে অদলবদল করুন

কোড

static class CopyOnWriteSwapper
{
    public static void Swap<T>(ref T obj, Func<T, T> cloner, Action<T> op)
        where T : class
    {
        while (true)
        {
            var objBefore = Volatile.Read(ref obj);
            var newObj = cloner(objBefore);
            op(newObj);
            if (Interlocked.CompareExchange(ref obj, newObj, objBefore) == objBefore)
                return;
        }
    }
}

ব্যবহার

CopyOnWriteSwapper.Swap(ref _myList,
    orig => new List<string>(orig),
    clone => clone.Add("asdf"));

আপনি আরো কর্মক্ষমতা প্রয়োজন হলে, এটা পদ্ধতি ungenerify করতে, যেমন পরিমার্জন প্রতি টাইপ জন্য এক পদ্ধতি তৈরি করতে সাহায্য করবে (সরান যোগ করুন,, ...) আপনি চান, এবং হার্ড কোড ফাংশন পয়েন্টার clonerএবং op

এনবি # 1 এটি নিশ্চিত করা আপনার দায়িত্ব যে কেউই (অনুমিত) অপরিবর্তনীয় ডেটা কাঠামো পরিবর্তন করে না। এটি রোধ করার জন্য আমরা জেনেরিক প্রয়োগে কিছুই করতে পারি না , তবে বিশেষীকরণের সময় List<T>আপনি তালিকাটি ব্যবহার করে সংশোধন থেকে রক্ষা করতে পারেন sএসরেডঅনলি ()

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

এনবি # 3 যদি আপনার ডেটা কাঠামো বিশাল হয় এবং আপনি এটি প্রায়শই সংশোধন করেন তবে অনুলিপি-অনুলিখনের পদ্ধতি মেমরির ব্যবহার এবং জড়িত সিপিইউয়ের সাথে জড়িত উভয় ক্ষেত্রেই নিষিদ্ধ হতে পারে। সেক্ষেত্রে আপনি এমএসের অপরিবর্তনীয় সংগ্রহগুলি ব্যবহার করতে চাইতে পারেন ।


3

System.Collections.Generic.List<t>ইতিমধ্যে একাধিক পাঠকের জন্য থ্রেড নিরাপদ। একাধিক লেখকের জন্য এটিকে থ্রেডটি নিরাপদ করার চেষ্টা করা বোধগম্য হবে না। (কারণেই হেনক এবং স্টিফেন ইতিমধ্যে উল্লেখ করেছেন)


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

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

আপনি ঠিক বলেছেন - আমি সূচিযুক্ত অ্যাক্সেস চাই না। আমি সাধারণত আইলিস্ট <T> ব্যবহার করতে পারি তার জন্য একটি প্রিন্সি হিসাবে যেটি আমি করতে পারি। নতুন টি উপাদান যুক্ত করুন (টি)। প্রশ্নটি এখান থেকেই এসেছে।
অ্যালান

@ অ্যালান: তারপরে আপনি একটি সারি চান, তালিকা নয়।
বিলি ওনিল

3
আমি আপনি ভুল মনে হয়। বলা: একাধিক পাঠকের পক্ষে নিরাপদ হওয়ার অর্থ এই নয় যে আপনি একই সাথে লিখতে পারবেন না। লেখার অর্থ মুছে ফেলাও বোঝায় এবং এতে পুনরাবৃত্তি হওয়ার সময় আপনি মুছে ফেললে আপনি একটি ত্রুটি পাবেন।
এরিক ওয়েললেট

2

কিছু লোক কিছু পণ্য পয়েন্ট (এবং আমার কিছু চিন্তা) hillight:

  • এটি এলোমেলো অ্যাক্সেসার (সূচক) অক্ষম করতে পাগলের মতো দেখতে পারে তবে আমার কাছে এটি দুর্দান্ত দেখা যায়। আপনাকে কেবল ভাবতে হবে যে মাল্টি-থ্রেডযুক্ত সংগ্রহগুলিতে এমন অনেকগুলি পদ্ধতি রয়েছে যা সূচক এবং মুছার মতো ব্যর্থ হতে পারে। আপনি "ব্যর্থ" বা সহজভাবে "শেষে যুক্ত করুন" এর মতো রাইট অ্যাকসেসরের জন্য ব্যর্থতা (ফলব্যাক) ক্রিয়াও সংজ্ঞায়িত করতে পারেন।
  • এটি কোনও মাল্টিথ্রেডেড সংগ্রহ নয় কারণ এটি সর্বদা একটি বহুবিশ্লেষিত প্রসঙ্গে ব্যবহৃত হবে। অথবা এটি কেবলমাত্র একজন লেখক এবং একজন পাঠকই ব্যবহার করতে পারেন।
  • নিরাপদ উপায়ে ইন্ডেক্সার ব্যবহার করতে সক্ষম হওয়ার আরেকটি উপায় হ'ল সংগ্রহটির মূলটি (যদি প্রকাশ্যে করা হয়) ব্যবহার করে সংগ্রহের একটি লকটিতে ক্রিয়াগুলি लपेटানো।
  • অনেক লোকের জন্য, একটি রুটলক দৃশ্যমান করা চালক "ভাল অনুশীলন" হয়। আমি এই পয়েন্টটি সম্পর্কে 100% নিশ্চিত নই কারণ এটি লুকিয়ে থাকলে আপনি ব্যবহারকারীর অনেকটা নমনীয়তা সরিয়ে ফেলেন। আমাদের সর্বদা মনে রাখতে হবে যে বহুগঠিত প্রোগ্রামিং কারও জন্য নয়। আমরা প্রতিটি ধরণের ভুল ব্যবহার রোধ করতে পারি না।
  • মাইক্রোসফ্টকে মাল্টিথ্রেডেড সংগ্রহের সঠিক ব্যবহার প্রবর্তনের জন্য কিছু কাজ করতে হবে এবং কিছু নতুন মান নির্ধারণ করতে হবে। প্রথমে আইনিমরেটরটির মুভনেক্সট থাকা উচিত নয় তবে একটি গেইনেক্সট থাকা উচিত যা সত্য বা মিথ্যা ফিরে আসে এবং টাইপের টি এর আউট প্যারামিটার পাওয়া উচিত (এইভাবে পুনরাবৃত্তিটি আর ব্লক হবে না)। এছাড়াও, মাইক্রোসফ্ট ইতিমধ্যে "পূর্বে" অভ্যন্তরীণভাবে "ব্যবহার" ব্যবহার করে তবে কখনও কখনও আইনিউমরেটরটিকে "ব্যবহার করে" সংগ্রহ না করে সরাসরি সংগ্রহ করে (সংগ্রহের দৃশ্যে একটি বাগ এবং সম্ভবত আরও স্থানে) - মাইক্রোসফ্টের দ্বারা আইপুনেটর র‌্যাপিং ব্যবহার একটি প্রস্তাবিত প্রীতি। এই বাগটি নিরাপদ পুনরাবৃত্তির জন্য ভাল সম্ভাব্যতা সরিয়ে দেয় ... ইল্ট্রেটর যা সংগ্রহকে কন্ট্রাক্টরে লক করে এবং এর নিষ্পত্তি পদ্ধতিতে আনলক করে - একটি ব্লকিং ফোরচ পদ্ধতির জন্য।

এটি কোনও উত্তর নয়। এটি কেবলমাত্র এমন মন্তব্য যা কোনও নির্দিষ্ট জায়গার সাথে সত্যই খাপ খায় না।

... আমার উপসংহারে, মাইক্রোসফ্টকে মাল্টিথ্রেডেড সংগ্রহ সহজতর করতে "ফরচ" তে কিছু গভীর পরিবর্তন করতে হবে। এছাড়াও এটিতে আইনিমেটর ব্যবহারের নিজস্ব বিধিগুলি অনুসরণ করতে হবে। ততক্ষণে আমরা সহজেই একটি মাল্টিথ্রেডলিস্ট লিখতে পারি যা একটি ব্লকিং পুনরায় ব্যবহার করতে পারে তবে এটি "আইলিস্ট" অনুসরণ করবে না। পরিবর্তে, আপনাকে নিজস্ব "IListPersonnal" ইন্টারফেসটি সংজ্ঞায়িত করতে হবে যা ব্যতীত "সন্নিবেশ", "অপসারণ" এবং র্যান্ডম অ্যাকসেসর (সূচক) ব্যর্থ হতে পারে। তবে এটি মানক না হলে কে এটি ব্যবহার করতে চাইবে?


যে কেউ সহজেই এমন একটি লিখতে পারে ConcurrentOrderedBag<T>যার মধ্যে কেবলমাত্র পঠনযোগ্য বাস্তবায়ন অন্তর্ভুক্ত থাকে IList<T>তবে এটি সম্পূর্ণ-থ্রেড-নিরাপদ int Add(T value)পদ্ধতিও সরবরাহ করে। কেন কোনও ForEachপরিবর্তন প্রয়োজন হবে তা আমি দেখছি না। যদিও মাইক্রোসফ্ট স্পষ্টভাবে এটি না বলে, তাদের অনুশীলন থেকে বোঝা যায় যে IEnumerator<T>এটি তৈরির সময় উপস্থিত সামগ্রীর সামগ্রীগুলি গণনা করার জন্য এটি পুরোপুরি গ্রহণযোগ্য ; সংগ্রহ-সংশোধিত ব্যতিক্রম কেবলমাত্র তখনই প্রয়োজন যদি গণকরা গ্লিট-ফ্রি অপারেশনের গ্যারান্টি দিতে সক্ষম হয় না।
সুপারক্যাট

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

অন্যান্য জিনিসটিও ঘটতে পারে তা হ'ল আপনি যখন কোনও পুনরাবৃত্তি পেলেন তখন আপনি সংগ্রহটি লক করতে পারেন এবং যখন আপনার পুনরুক্তি করা হয় জিসি সংগ্রহ করা হয় আপনি সংগ্রহটি আনলক করতে পারেন। মাইক্রোসফটের মতে তারা ইতিমধ্যে আইইনামারবেলটিও একটি আইডিজपोজযোগ্য কিনা তা পরীক্ষা করে দেখে ফোরইচের শেষে জিসিকে ফোন করুন। মূল সমস্যাটি হ'ল তারা জিসিকে কল না করে অন্য কোথাও আইনিউমারবল ব্যবহার করে, আপনি তারপরে নির্ভর করতে পারবেন না। আইনিম্যারেবল সক্ষম সক্ষম লকটির জন্য একটি নতুন স্পষ্ট এমটি ইন্টারফেস থাকা সমস্যার কমপক্ষে একটি অংশ সমাধান করতে পারে part (এটি লোকেদের কল করতে বাধা দেয় না)।
এরিক ওয়েললেট

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

IEnumerable<T>রিটার্নের ধরণ সহ একটি "স্ন্যাপশট" পদ্ধতি অন্তর্ভুক্ত থাকলে সবচেয়ে বেশি কী সহায়ক হত IEnumerable<T>। অপরিষ্কার সংগ্রহগুলি তারা নিজেরাই ফিরে আসতে পারে; একটি উত্তরে সংগ্রহে কিছুই অন্য একটি থেকে নিজেকে কপি যেত List<T>বা T[]এবং কল GetEnumeratorযে। কিছু আনবাউন্ডেড সংগ্রহগুলি কার্যকর করতে পারে Snapshotএবং যা সেগুলি তাদের বিষয়বস্তু সহ একটি তালিকা পূরণ করার চেষ্টা না করে ব্যতিক্রম ছুঁড়ে ফেলতে পারে না।
সুপারক্যাট

1

ধারাবাহিকভাবে সম্পাদনকারী কোডে ব্যবহৃত ডেটা স্ট্রাকচারগুলি (ভালভাবে লেখা) একযোগে সম্পাদনকারী কোডের চেয়ে পৃথক। কারণটি হ'ল ক্রমযুক্ত কোডটি অন্তর্নিহিত আদেশকে বোঝায়। সমকালীন কোড তবে কোনও আদেশ বোঝায় না; আরও ভাল এটি কোনও সংজ্ঞায়িত আদেশের অভাব বোঝায়!

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

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


1

লকলেস অনুলিপি এবং লিখন পদ্ধতির দুর্দান্ত কাজ করে যদি আপনি অনেকগুলি আইটেমের সাথে ডিল করেন না। এখানে আমি লিখেছি এমন একটি ক্লাস:

public class CopyAndWriteList<T>
{
    public static List<T> Clear(List<T> list)
    {
        var a = new List<T>(list);
        a.Clear();
        return a;
    }

    public static List<T> Add(List<T> list, T item)
    {
        var a = new List<T>(list);
        a.Add(item);
        return a;
    }

    public static List<T> RemoveAt(List<T> list, int index)
    {
        var a = new List<T>(list);
        a.RemoveAt(index);
        return a;
    }

    public static List<T> Remove(List<T> list, T item)
    {
        var a = new List<T>(list);
        a.Remove(item);
        return a;
    }

}

উদাহরণস্বরূপ ব্যবহার: আদেশ_BUY = অনুলিপিআরডওরাইটলিস্ট.ক্লেয়ার (অর্ডার_বিইউই);


লক করার পরিবর্তে এটি তালিকার একটি অনুলিপি তৈরি করে, তালিকাটি পরিবর্তন করে এবং নতুন তালিকার রেফারেন্স সেট করে। সুতরাং পুনরুক্তিযুক্ত অন্য কোনও থ্রেড কোনও সমস্যা সৃষ্টি করবে না।
কোয়ান্ট

0

আমি ব্রায়ান এর অনুরূপ একটি বাস্তবায়ন । খনি আলাদা:

  • আমি সরাসরি অ্যারে পরিচালনা করি।
  • আমি চেষ্টা ব্লকের মধ্যে লক প্রবেশ করি না।
  • আমি yield returnএকটি গণক উত্পাদন জন্য ব্যবহার করি ।
  • আমি লক পুনরাবৃত্তি সমর্থন করি। এটি পুনরাবৃত্তির সময় তালিকা থেকে পাঠের অনুমতি দেয়।
  • আমি যেখানে সম্ভব সেখানে আপগ্রেডেবল রিড লক ব্যবহার করি।
  • DoSyncএবং GetSyncপদ্ধতিগুলি ক্রমিক ক্রিয়াকলাপের অনুমতি দেয় যা তালিকায় একচেটিয়া অ্যাক্সেসের প্রয়োজন।

কোড :

public class ConcurrentList<T> : IList<T>, IDisposable
{
    private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
    private int _count = 0;

    public int Count
    {
        get
        { 
            _lock.EnterReadLock();
            try
            {           
                return _count;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
    }

    public int InternalArrayLength
    { 
        get
        { 
            _lock.EnterReadLock();
            try
            {           
                return _arr.Length;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
    }

    private T[] _arr;

    public ConcurrentList(int initialCapacity)
    {
        _arr = new T[initialCapacity];
    }

    public ConcurrentList():this(4)
    { }

    public ConcurrentList(IEnumerable<T> items)
    {
        _arr = items.ToArray();
        _count = _arr.Length;
    }

    public void Add(T item)
    {
        _lock.EnterWriteLock();
        try
        {       
            var newCount = _count + 1;          
            EnsureCapacity(newCount);           
            _arr[_count] = item;
            _count = newCount;                  
        }
        finally
        {
            _lock.ExitWriteLock();
        }       
    }

    public void AddRange(IEnumerable<T> items)
    {
        if (items == null)
            throw new ArgumentNullException("items");

        _lock.EnterWriteLock();

        try
        {           
            var arr = items as T[] ?? items.ToArray();          
            var newCount = _count + arr.Length;
            EnsureCapacity(newCount);           
            Array.Copy(arr, 0, _arr, _count, arr.Length);       
            _count = newCount;
        }
        finally
        {
            _lock.ExitWriteLock();          
        }
    }

    private void EnsureCapacity(int capacity)
    {   
        if (_arr.Length >= capacity)
            return;

        int doubled;
        checked
        {
            try
            {           
                doubled = _arr.Length * 2;
            }
            catch (OverflowException)
            {
                doubled = int.MaxValue;
            }
        }

        var newLength = Math.Max(doubled, capacity);            
        Array.Resize(ref _arr, newLength);
    }

    public bool Remove(T item)
    {
        _lock.EnterUpgradeableReadLock();

        try
        {           
            var i = IndexOfInternal(item);

            if (i == -1)
                return false;

            _lock.EnterWriteLock();
            try
            {   
                RemoveAtInternal(i);
                return true;
            }
            finally
            {               
                _lock.ExitWriteLock();
            }
        }
        finally
        {           
            _lock.ExitUpgradeableReadLock();
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        _lock.EnterReadLock();

        try
        {    
            for (int i = 0; i < _count; i++)
                // deadlocking potential mitigated by lock recursion enforcement
                yield return _arr[i]; 
        }
        finally
        {           
            _lock.ExitReadLock();
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    public int IndexOf(T item)
    {
        _lock.EnterReadLock();
        try
        {   
            return IndexOfInternal(item);
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    private int IndexOfInternal(T item)
    {
        return Array.FindIndex(_arr, 0, _count, x => x.Equals(item));
    }

    public void Insert(int index, T item)
    {
        _lock.EnterUpgradeableReadLock();

        try
        {                       
            if (index > _count)
                throw new ArgumentOutOfRangeException("index"); 

            _lock.EnterWriteLock();
            try
            {       
                var newCount = _count + 1;
                EnsureCapacity(newCount);

                // shift everything right by one, starting at index
                Array.Copy(_arr, index, _arr, index + 1, _count - index);

                // insert
                _arr[index] = item;     
                _count = newCount;
            }
            finally
            {           
                _lock.ExitWriteLock();
            }
        }
        finally
        {
            _lock.ExitUpgradeableReadLock();            
        }


    }

    public void RemoveAt(int index)
    {   
        _lock.EnterUpgradeableReadLock();
        try
        {   
            if (index >= _count)
                throw new ArgumentOutOfRangeException("index");

            _lock.EnterWriteLock();
            try
            {           
                RemoveAtInternal(index);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        finally
        {
            _lock.ExitUpgradeableReadLock();            
        }
    }

    private void RemoveAtInternal(int index)
    {           
        Array.Copy(_arr, index + 1, _arr, index, _count - index-1);
        _count--;

        // release last element
        Array.Clear(_arr, _count, 1);
    }

    public void Clear()
    {
        _lock.EnterWriteLock();
        try
        {        
            Array.Clear(_arr, 0, _count);
            _count = 0;
        }
        finally
        {           
            _lock.ExitWriteLock();
        }   
    }

    public bool Contains(T item)
    {
        _lock.EnterReadLock();
        try
        {   
            return IndexOfInternal(item) != -1;
        }
        finally
        {           
            _lock.ExitReadLock();
        }
    }

    public void CopyTo(T[] array, int arrayIndex)
    {       
        _lock.EnterReadLock();
        try
        {           
            if(_count > array.Length - arrayIndex)
                throw new ArgumentException("Destination array was not long enough.");

            Array.Copy(_arr, 0, array, arrayIndex, _count);
        }
        finally
        {
            _lock.ExitReadLock();           
        }
    }

    public bool IsReadOnly
    {   
        get { return false; }
    }

    public T this[int index]
    {
        get
        {
            _lock.EnterReadLock();
            try
            {           
                if (index >= _count)
                    throw new ArgumentOutOfRangeException("index");

                return _arr[index]; 
            }
            finally
            {
                _lock.ExitReadLock();               
            }           
        }
        set
        {
            _lock.EnterUpgradeableReadLock();
            try
            {

                if (index >= _count)
                    throw new ArgumentOutOfRangeException("index");

                _lock.EnterWriteLock();
                try
                {                       
                    _arr[index] = value;
                }
                finally
                {
                    _lock.ExitWriteLock();              
                }
            }
            finally
            {
                _lock.ExitUpgradeableReadLock();
            }

        }
    }

    public void DoSync(Action<ConcurrentList<T>> action)
    {
        GetSync(l =>
        {
            action(l);
            return 0;
        });
    }

    public TResult GetSync<TResult>(Func<ConcurrentList<T>,TResult> func)
    {
        _lock.EnterWriteLock();
        try
        {           
            return func(this);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    public void Dispose()
    {   
        _lock.Dispose();
    }
}

দুটি থ্রেড যদি একই সময়ে tryব্লক ইন Removeবা সূচক সেটারের শুরুতে আসে তবে কী হবে ?
জেমস

@ জেমস যা সম্ভব বলে মনে হচ্ছে না। এমএসডিএন.মাইক্রোসফট /en-us/library/ … এ মন্তব্য পড়ুন । এই কোডটি চালানো হচ্ছে, আপনি কখনই সেই লকটি দ্বিতীয়বার প্রবেশ করতে পারবেন না: gist.github.com/ronnieoverby/59b715c3676127a113c3
রনি ওভারবি

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

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

1
আমি এই রেকর্ডে যেতে চেয়েছিলাম যে আমি স্বীকৃতি দিয়েছি যে IListসমবর্তী পরিস্থিতিগুলিতে শব্দার্থবিদ্যার উপযোগিতা সীমাবদ্ধ। আমি এই কোডটি সম্ভবত এই উপলব্ধিতে আসার আগে লিখেছিলাম। আমার অভিজ্ঞতা গ্রহণযোগ্য উত্তরের লেখকের মতো একই: আমি এটি সিঙ্ক্রোনাইজেশন এবং আইলিস্ট <T> সম্পর্কে যা জানলাম তা দিয়ে চেষ্টা করেছি এবং এটি করে আমি কিছু শিখেছি।
রনি ওভারবাই
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.