স্থির আকারের সারি যা স্বয়ংক্রিয়ভাবে নতুন এনকগুলিতে পুরানো মানগুলির প্রাপ্য


120

আমি ConcurrentQueueএকটি ভাগ করা ডেটা স্ট্রাকচারের জন্য ব্যবহার করছি যা শেষ এন বস্তুগুলিকে এতে পাস করা হয়েছে (ইতিহাসের ধরণের)।

ধরে নিন আমাদের একটি ব্রাউজার রয়েছে এবং আমরা সর্বশেষ 100 ব্রাউজ করা url পেতে চাই। আমি একটি সারি চাই যেটি স্বয়ংক্রিয়ভাবে নতুন প্রবেশের সন্নিবেশ (এনকুই) এর সাথে সবচেয়ে পুরানো (প্রথম) এন্ট্রিটি ড্রপ করে (যখন ইতিহাসের 100 টি ঠিকানা)।

আমি কীভাবে এটি ব্যবহার করে সম্পন্ন করতে পারি System.Collections?



এটি আপনার জন্য বিশেষভাবে বোঝানো হয়নি, তবে যে কেউ এই প্রশ্নটি আসে এবং এটি কার্যকর মনে হতে পারে। বিটিডব্লিউ, এটি সি # সম্পর্কেও কথা বলে। আপনি কি সমস্ত উত্তর (2 মিনিটের মধ্যে) পড়ে এবং বুঝতে পেরেছিলেন যে সেখানে কোনও # সি কোড নেই? যাইহোক, আমি নিজেই নিশ্চিত নই, এবং এ কারণেই এটি একটি মন্তব্য ...

আপনি কেবল পদ্ধতিগুলিকে একটি লকে মোড়াতে পারেন। এগুলি দেওয়া যে দ্রুত, আপনি কেবল পুরো অ্যারে লক করতে পারেন। যদিও এটি সম্ভবত একটি ডুপ। সি # কোড দিয়ে বিজ্ঞপ্তিযুক্ত বাফার বাস্তবায়নের জন্য আপনাকে কিছু খুঁজে পেতে পারে। যাই হোক, শুভকামনা।

উত্তর:


110

আমি একটি মোড়কের ক্লাস লিখব যে এনেকুতে গণনা চেক করবে এবং তারপরে গণনা সীমা ছাড়িয়ে যাবে que

 public class FixedSizedQueue<T>
 {
     ConcurrentQueue<T> q = new ConcurrentQueue<T>();
     private object lockObject = new object();

     public int Limit { get; set; }
     public void Enqueue(T obj)
     {
        q.Enqueue(obj);
        lock (lockObject)
        {
           T overflow;
           while (q.Count > Limit && q.TryDequeue(out overflow)) ;
        }
     }
 }

4
qঅবজেক্টটির জন্য ব্যক্তিগত, যাতে lockএকসাথে অন্যান্য থ্রেডগুলি প্রতিরোধ করে prevent
রিচার্ড স্নাইডার

14
লক করা ভাল ধারণা নয়। ছাত্রলীগের সমকালীন সংগ্রহের পুরো উদ্দেশ্যটি পারফরম্যান্সের কারণে লক ফ্রি সমঝোতা সরবরাহ করা। আপনার কোডে লক করা সেই সুবিধাটিকে আপস করে। আসলে আপনাকে ডেকে লক করার কোনও কারণ আমি দেখতে পাচ্ছি না।
কেএফএল

2
@ কেএফএল, লক করা দরকার কারণ Countএবং TryDequeueদুটি স্বতন্ত্র অপারেশন যা বিসিএল সমকালীন দ্বারা সিঙ্ক হয় না যত্ন করে।
রিচার্ড স্নাইডার

9
@ রিচার্ডশিনিডার আপনার যদি সম্মতি সংক্রান্ত বিষয়গুলি নিজেই পরিচালনা করতে হয় তবে কোনও হালকা ওজনের যাবতীয় ConcurrentQueue<T>বস্তুর জন্য বস্তুটি অদলবদল করা ভাল ধারণা Queue<T>
0b101010

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

104

আমি কিছুটা বৈকল্পিকের জন্য যাব ... সাম্প্রতিক কুইউ বাড়িয়েছি যাতে ফিক্সডসাইজ কিউয়ে লিনক এক্সটেনশনগুলি ব্যবহার করতে সক্ষম হতে

public class FixedSizedQueue<T> : ConcurrentQueue<T>
{
    private readonly object syncObject = new object();

    public int Size { get; private set; }

    public FixedSizedQueue(int size)
    {
        Size = size;
    }

    public new void Enqueue(T obj)
    {
        base.Enqueue(obj);
        lock (syncObject)
        {
            while (base.Count > Size)
            {
                T outObj;
                base.TryDequeue(out outObj);
            }
        }
    }
}

1
যখন কেউ স্থিতিশীলভাবে কনকেন্যর কিউ <টি> হিসাবে উদাহরণটি জানে তখন কী হয়, তারা কেবল আপনার 'নতুন' কীওয়ার্ডটি ছড়িয়ে দিয়েছে।
সোমবার

6
@ মিণ্ড যদি 'কেউ' তা করতে চায়; তারপরে তারা শুরু করার জন্য একটি সাম্প্রতিক কিউ <টি> অবজেক্ট ব্যবহার করা পছন্দ করত ... এটি একটি কাস্টম স্টোরেজ শ্রেণি। এটি নেট। ফ্রেমওয়ার্কে জমা দেওয়ার জন্য কেউ চাইছেন না। আপনি এটির জন্য সমস্যা তৈরি করার চেষ্টা করেছেন।
ডেভ লরেন্স

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

3
@ হ্যাঁ হ্যাঁ আপনি যা বলছেন তা আমি পেয়েছি .. আমি একটি সারি গুটিয়ে রাখতে পারি এবং সারিটির তালিকাটি প্রকাশ করতে পারতাম যাতে লিনক এক্সটেনশনগুলি ব্যবহার করতে পারি।
ডেভ লরেন্স

1
আমি @ মিণ্ডের সাথে একমত হয়েছি যে আপনি সমকালীন কিউয়ের উত্তরাধিকারী হবেন না কারণ এনেকু পদ্ধতিটি ভার্চুয়াল নয়। আপনার সারিটি প্রক্সি করুন এবং যদি ইচ্ছা হয় তবে পুরো ইন্টারফেসটি প্রয়োগ করুন।
ক্রিস মেরিসিক

29

যে কারও পক্ষে এটি দরকারী মনে হয়, উপরে রিচার্ড স্নাইডারের উত্তরের উপর ভিত্তি করে কিছু কার্য কোড রয়েছে:

public class FixedSizedQueue<T>
{
    readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();

    public int Size { get; private set; }

    public FixedSizedQueue(int size)
    {
        Size = size;
    }

    public void Enqueue(T obj)
    {
        queue.Enqueue(obj);

        while (queue.Count > Size)
        {
            T outObj;
            queue.TryDequeue(out outObj);
        }
    }
}

1
এটি সত্য সংগ্রহের জন্য প্রয়োজনীয় ইন্টারফেসগুলির কোনও বাস্তবায়ন না করা ছাড়াও উল্লিখিত কারণে (কনকন্ট্রিউ কিউ ব্যবহার করার সময় লক করা খারাপ) এর জন্য ভোট প্রদান।
জোশ

11

এর মূল্য কী, এটি নিরাপদ এবং অনিরাপদ ব্যবহারের জন্য চিহ্নিত কয়েকটি পদ্ধতির সাথে হালকা ওজনের একটি বিজ্ঞপ্তিযুক্ত বাফার।

public class CircularBuffer<T> : IEnumerable<T>
{
    readonly int size;
    readonly object locker;

    int count;
    int head;
    int rear;
    T[] values;

    public CircularBuffer(int max)
    {
        this.size = max;
        locker = new object();
        count = 0;
        head = 0;
        rear = 0;
        values = new T[size];
    }

    static int Incr(int index, int size)
    {
        return (index + 1) % size;
    }

    private void UnsafeEnsureQueueNotEmpty()
    {
        if (count == 0)
            throw new Exception("Empty queue");
    }

    public int Size { get { return size; } }
    public object SyncRoot { get { return locker; } }

    #region Count

    public int Count { get { return UnsafeCount; } }
    public int SafeCount { get { lock (locker) { return UnsafeCount; } } }
    public int UnsafeCount { get { return count; } }

    #endregion

    #region Enqueue

    public void Enqueue(T obj)
    {
        UnsafeEnqueue(obj);
    }

    public void SafeEnqueue(T obj)
    {
        lock (locker) { UnsafeEnqueue(obj); }
    }

    public void UnsafeEnqueue(T obj)
    {
        values[rear] = obj;

        if (Count == Size)
            head = Incr(head, Size);
        rear = Incr(rear, Size);
        count = Math.Min(count + 1, Size);
    }

    #endregion

    #region Dequeue

    public T Dequeue()
    {
        return UnsafeDequeue();
    }

    public T SafeDequeue()
    {
        lock (locker) { return UnsafeDequeue(); }
    }

    public T UnsafeDequeue()
    {
        UnsafeEnsureQueueNotEmpty();

        T res = values[head];
        values[head] = default(T);
        head = Incr(head, Size);
        count--;

        return res;
    }

    #endregion

    #region Peek

    public T Peek()
    {
        return UnsafePeek();
    }

    public T SafePeek()
    {
        lock (locker) { return UnsafePeek(); }
    }

    public T UnsafePeek()
    {
        UnsafeEnsureQueueNotEmpty();

        return values[head];
    }

    #endregion


    #region GetEnumerator

    public IEnumerator<T> GetEnumerator()
    {
        return UnsafeGetEnumerator();
    }

    public IEnumerator<T> SafeGetEnumerator()
    {
        lock (locker)
        {
            List<T> res = new List<T>(count);
            var enumerator = UnsafeGetEnumerator();
            while (enumerator.MoveNext())
                res.Add(enumerator.Current);
            return res.GetEnumerator();
        }
    }

    public IEnumerator<T> UnsafeGetEnumerator()
    {
        int index = head;
        for (int i = 0; i < count; i++)
        {
            yield return values[index];
            index = Incr(index, size);
        }
    }

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

    #endregion
}

আমি Foo()/SafeFoo()/UnsafeFoo()সম্মেলনটি ব্যবহার করতে পছন্দ করি :

  • Fooপদ্ধতিগুলি UnsafeFooডিফল্ট হিসাবে কল করে ।
  • UnsafeFoo পদ্ধতিগুলি লক ছাড়াই অবাধে রাষ্ট্র পরিবর্তন করে, তাদের কেবল অন্য অনিরাপদ পদ্ধতিতে কল করা উচিত।
  • SafeFooপদ্ধতিগুলি UnsafeFooএকটি লকের ভিতরে কল পদ্ধতিগুলি।

এটি সামান্য ভার্বোজ, তবে এটি সুস্পষ্ট ত্রুটিগুলি তৈরি করে, যেমন কোনও পদ্ধতিতে তালার বাইরে অনিরাপদ পদ্ধতিগুলি কল করা যা থ্রেড-নিরাপদ বলে মনে করা হয়, আরও স্পষ্ট।


5

এখানে আমার স্থির আকারের কাতারে নেওয়া

Countসম্পত্তিটি ব্যবহৃত হয় যখন সিঙ্ক্রোনাইজেশন ওভারহেড এড়ানোর জন্য এটি নিয়মিত সারি ব্যবহার করে ConcurrentQueue। এটি কার্যকর করে IReadOnlyCollectionযাতে লিনকিউ পদ্ধতি ব্যবহার করা যায়। বাকিগুলি এখানে অন্যান্য উত্তরগুলির সাথে খুব মিল।

[Serializable]
[DebuggerDisplay("Count = {" + nameof(Count) + "}, Limit = {" + nameof(Limit) + "}")]
public class FixedSizedQueue<T> : IReadOnlyCollection<T>
{
    private readonly Queue<T> _queue = new Queue<T>();
    private readonly object _lock = new object();

    public int Count { get { lock (_lock) { return _queue.Count; } } }
    public int Limit { get; }

    public FixedSizedQueue(int limit)
    {
        if (limit < 1)
            throw new ArgumentOutOfRangeException(nameof(limit));

        Limit = limit;
    }

    public FixedSizedQueue(IEnumerable<T> collection)
    {
        if (collection is null || !collection.Any())
           throw new ArgumentException("Can not initialize the Queue with a null or empty collection", nameof(collection));

        _queue = new Queue<T>(collection);
        Limit = _queue.Count;
    }

    public void Enqueue(T obj)
    {
        lock (_lock)
        {
            _queue.Enqueue(obj);

            while (_queue.Count > Limit)
                _queue.Dequeue();
        }
    }

    public void Clear()
    {
        lock (_lock)
            _queue.Clear();
    }

    public IEnumerator<T> GetEnumerator()
    {
        lock (_lock)
            return new List<T>(_queue).GetEnumerator();
    }

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

3

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

public class FixedSizeQueue<T> : IReadOnlyCollection<T>
{
  private ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
  private int _count;

  public int Limit { get; private set; }

  public FixedSizeQueue(int limit)
  {
    this.Limit = limit;
  }

  public void Enqueue(T obj)
  {
    _queue.Enqueue(obj);
    Interlocked.Increment(ref _count);

    // Calculate the number of items to be removed by this thread in a thread safe manner
    int currentCount;
    int finalCount;
    do
    {
      currentCount = _count;
      finalCount = Math.Min(currentCount, this.Limit);
    } while (currentCount != 
      Interlocked.CompareExchange(ref _count, finalCount, currentCount));

    T overflow;
    while (currentCount > finalCount && _queue.TryDequeue(out overflow))
      currentCount--;
  }

  public int Count
  {
    get { return _count; }
  }

  public IEnumerator<T> GetEnumerator()
  {
    return _queue.GetEnumerator();
  }

  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  {
    return _queue.GetEnumerator();
  }
}

1
একযোগে ব্যবহার করা থাকলে এটি ভাঙা হয়েছে - কল _queue.Enqueue(obj)করার পরে যদি কোনও থ্রেড প্রম্পট করা হয় Interlocked.Increment(ref _count)এবং অন্য থ্রেড কল করে তবে .Countকী হবে? এটি একটি ভুল গণনা পেতে হবে। আমি অন্যান্য সমস্যাগুলির জন্য পরীক্ষা করে দেখিনি।
কেএফএল

3

আমার সংস্করণটি কেবলমাত্র একটি সাধারণ সাবক্লাস Queue.. এটিতে অংশ নেওয়া সবাইকে দেখে বিশেষ কিছুই নয় এবং এটি এখনও শিরোনামের শিরোনামটি সহ আমি এখানে রেখেছি। এটি কেবল ক্ষেত্রে প্রাপ্যকে ফিরিয়ে দেয়।

public sealed class SizedQueue<T> : Queue<T>
{
    public int FixedCapacity { get; }
    public SizedQueue(int fixedCapacity)
    {
        this.FixedCapacity = fixedCapacity;
    }

    /// <summary>
    /// If the total number of item exceed the capacity, the oldest ones automatically dequeues.
    /// </summary>
    /// <returns>The dequeued value, if any.</returns>
    public new T Enqueue(T item)
    {
        base.Enqueue(item);
        if (base.Count > FixedCapacity)
        {
            return base.Dequeue();
        }
        return default;
    }
}

2

এর আরও একটি উত্তর যুক্ত করা যাক। কেন অন্যের উপর এই?

1) সরলতা। আকারের গ্যারান্টি দেওয়ার চেষ্টা করা ভাল এবং ভাল তবে অনাঙ্কিত জটিলতার দিকে নিয়ে যায় যা এর নিজস্ব সমস্যাগুলি প্রদর্শন করতে পারে।

2) আইআরডঅনলি ক্লেকশন কার্যকর করে, এর অর্থ আপনি এর উপর লিনক ব্যবহার করতে পারেন এবং এটি বিভিন্ন ধরণের জিনিসগুলিতে পাস করতে পারেন যা প্রত্যাশাযোগ্য প্রত্যাশা করে।

3) কোন লক না। উপরের অনেকগুলি সমাধান লক ব্যবহার করে যা লকলেস সংগ্রহের ক্ষেত্রে ভুল।

4) একই পদ্ধতি, বৈশিষ্ট্য এবং ইন্টারফেসগুলি সমকালীন কিউইউগুলি সহ কার্যকর করে, আইপিড্রোডসারসনামার কালেকশন সহ, যা আপনি ব্লকিংকলেকশন সহ সংগ্রহটি ব্যবহার করতে চান তা গুরুত্বপূর্ণ।

এই বাস্তবায়নটি সম্ভাব্যভাবে প্রত্যাশার চেয়ে বেশি এন্ট্রি দিয়ে শেষ করতে পারে যদি ট্রিডিকিউ ব্যর্থ হয় তবে এই সংঘটিত হওয়ার ফ্রিকোয়েন্সি বিশেষায়িত কোডের মতো বলে মনে হয় না যা অনিবার্যভাবে সম্পাদনকে বাধাগ্রস্থ করবে এবং তার নিজস্ব অপ্রত্যাশিত সমস্যা তৈরি করবে।

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

class ConcurrentFixedSizeQueue<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>, ICollection {
    readonly ConcurrentQueue<T> m_concurrentQueue;
    readonly int m_maxSize;

    public int Count => m_concurrentQueue.Count;
    public bool IsEmpty => m_concurrentQueue.IsEmpty;

    public ConcurrentFixedSizeQueue (int maxSize) : this(Array.Empty<T>(), maxSize) { }

    public ConcurrentFixedSizeQueue (IEnumerable<T> initialCollection, int maxSize) {
        if (initialCollection == null) {
            throw new ArgumentNullException(nameof(initialCollection));
        }

        m_concurrentQueue = new ConcurrentQueue<T>(initialCollection);
        m_maxSize = maxSize;
    }

    public void Enqueue (T item) {
        m_concurrentQueue.Enqueue(item);

        if (m_concurrentQueue.Count > m_maxSize) {
            T result;
            m_concurrentQueue.TryDequeue(out result);
        }
    }

    public void TryPeek (out T result) => m_concurrentQueue.TryPeek(out result);
    public bool TryDequeue (out T result) => m_concurrentQueue.TryDequeue(out result);

    public void CopyTo (T[] array, int index) => m_concurrentQueue.CopyTo(array, index);
    public T[] ToArray () => m_concurrentQueue.ToArray();

    public IEnumerator<T> GetEnumerator () => m_concurrentQueue.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator () => GetEnumerator();

    // Explicit ICollection implementations.
    void ICollection.CopyTo (Array array, int index) => ((ICollection)m_concurrentQueue).CopyTo(array, index);
    object ICollection.SyncRoot => ((ICollection) m_concurrentQueue).SyncRoot;
    bool ICollection.IsSynchronized => ((ICollection) m_concurrentQueue).IsSynchronized;

    // Explicit IProducerConsumerCollection<T> implementations.
    bool IProducerConsumerCollection<T>.TryAdd (T item) => ((IProducerConsumerCollection<T>) m_concurrentQueue).TryAdd(item);
    bool IProducerConsumerCollection<T>.TryTake (out T item) => ((IProducerConsumerCollection<T>) m_concurrentQueue).TryTake(out item);

    public override int GetHashCode () => m_concurrentQueue.GetHashCode();
    public override bool Equals (object obj) => m_concurrentQueue.Equals(obj);
    public override string ToString () => m_concurrentQueue.ToString();
}

1

আপনার কোডিং আনন্দের জন্য আমি আপনার কাছে জমা দিচ্ছি ' ConcurrentDeck' '

public class ConcurrentDeck<T>
{
   private readonly int _size;
   private readonly T[] _buffer;
   private int _position = 0;

   public ConcurrentDeck(int size)
   {
       _size = size;
       _buffer = new T[size];
   }

   public void Push(T item)
   {
       lock (this)
       {
           _buffer[_position] = item;
           _position++;
           if (_position == _size) _position = 0;
       }
   }

   public T[] ReadDeck()
   {
       lock (this)
       {
           return _buffer.Skip(_position).Union(_buffer.Take(_position)).ToArray();
       }
   }
}

ব্যবহারের উদাহরণ:

void Main()
{
    var deck = new ConcurrentDeck<Tuple<string,DateTime>>(25);
    var handle = new ManualResetEventSlim();
    var task1 = Task.Factory.StartNew(()=>{
    var timer = new System.Timers.Timer();
    timer.Elapsed += (s,a) => {deck.Push(new Tuple<string,DateTime>("task1",DateTime.Now));};
    timer.Interval = System.TimeSpan.FromSeconds(1).TotalMilliseconds;
    timer.Enabled = true;
    handle.Wait();
    }); 
    var task2 = Task.Factory.StartNew(()=>{
    var timer = new System.Timers.Timer();
    timer.Elapsed += (s,a) => {deck.Push(new Tuple<string,DateTime>("task2",DateTime.Now));};
    timer.Interval = System.TimeSpan.FromSeconds(.5).TotalMilliseconds;
    timer.Enabled = true;
    handle.Wait();
    }); 
    var task3 = Task.Factory.StartNew(()=>{
    var timer = new System.Timers.Timer();
    timer.Elapsed += (s,a) => {deck.Push(new Tuple<string,DateTime>("task3",DateTime.Now));};
    timer.Interval = System.TimeSpan.FromSeconds(.25).TotalMilliseconds;
    timer.Enabled = true;
    handle.Wait();
    }); 
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10));
    handle.Set();
    var outputtime = DateTime.Now;
    deck.ReadDeck().Select(d => new {Message = d.Item1, MilliDiff = (outputtime - d.Item2).TotalMilliseconds}).Dump(true);
}

1
আমি এই বাস্তবায়নটি পছন্দ করি তবে মনে রাখবেন যে যখন কেউ যুক্ত করা হয়নি তখন এটি ডিফল্ট (টি)
ড্যানিয়েল লিচ

আপনি যদি এই পদ্ধতিতে লক ব্যবহার করেন তবে আপনার পাঠকদের অগ্রাধিকার দেওয়ার জন্য আপনার পাঠক রাইটারলকস্লিম ব্যবহার করা উচিত।
জোশ

1

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

সম্পাদনা: যেহেতু আমরা ইতিমধ্যে আমাদের প্রয়োগের মধ্যে লকিং ব্যবহার করছি আমাদের প্রকৃতপক্ষে সাম্প্রতিক কুইয়ের দরকার নেই এটির কার্যকারিতা উন্নত হতে পারে।

class FixedSizedConcurrentQueue<T> 
{
    readonly Queue<T> queue = new Queue<T>();
    readonly object syncObject = new object();

    public int MaxSize { get; private set; }

    public FixedSizedConcurrentQueue(int maxSize)
    {
        MaxSize = maxSize;
    }

    public void Enqueue(T obj)
    {
        lock (syncObject)
        {
            queue.Enqueue(obj);
            while (queue.Count > MaxSize)
            {
                queue.Dequeue();
            }
        }
    }

    public T[] ToArray()
    {
        T[] result = null;
        lock (syncObject)
        {
            result = queue.ToArray();
        }

        return result;
    }

    public void Clear()
    {
        lock (syncObject)
        {
            queue.Clear();
        }
    }
}

সম্পাদনা: আমাদের সত্যিকারের syncObjectউপরের উদাহরণের প্রয়োজন নেই এবং আমরা বরং queueবস্তুটি ব্যবহার করতে পারি যেহেতু আমরা queueকোনও ফাংশনে পুনরায় আরম্ভ করছি না এবং এটি readonlyযেভাবেই চিহ্নিত করা হয়েছে ।


1

এখনও কেউ এটি না বলেছে কারণ আপনি একটি ব্যবহার করতে পারেন LinkedList<T>এবং থ্রেড সুরক্ষা যোগ করতে পারেন :

public class Buffer<T> : LinkedList<T>
{
    private int capacity;

    public Buffer(int capacity)
    {
        this.capacity = capacity;   
    }

    public void Enqueue(T item)
    {
        // todo: add synchronization mechanism
        if (Count == capacity) RemoveLast();
        AddFirst(item);
    }

    public T Dequeue()
    {
        // todo: add synchronization mechanism
        var last = Last.Value;
        RemoveLast();
        return last;
    }
}

একটি বিষয় লক্ষ্যণীয় হ'ল ডিফল্ট গণনার ক্রমটি এই উদাহরণে LIFO হবে। তবে প্রয়োজনে ওভাররাইড করা যেতে পারে।


0

গৃহীত উত্তরটি এড়ানো যায় এমন পার্শ্ব প্রতিক্রিয়া হতে চলেছে।

সূক্ষ্ম দানযুক্ত লকিং এবং লক-মুক্ত প্রক্রিয়া

নীচের লিঙ্কগুলি হল নীচে উল্লেখ করা হয় যেগুলি আমি নীচে আমার উদাহরণটি লিখেছিলাম।

মাইক্রোসফ্ট থেকে ডকুমেন্টেশন কিছুটা বিভ্রান্তিকর কারণ তারা লক ব্যবহার করে তবে তারা সেগমেন্ট ক্লাসগুলি লক করে দেয়। সেগমেন্ট ক্লাসগুলি নিজেরাই ইন্টারলকড ব্যবহার করে।

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace Lib.Core
{
    // Sources: 
    // https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/
    // https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked?view=netcore-3.1
    // https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueue.cs
    // https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs

    /// <summary>
    /// Concurrent safe circular buffer that will used a fixed capacity specified and resuse slots as it goes.
    /// </summary>
    /// <typeparam name="TObject">The object that you want to go into the slots.</typeparam>
    public class ConcurrentCircularBuffer<TObject>
    {
        private readonly ConcurrentQueue<TObject> _queue;

        public int Capacity { get; private set; }

        public ConcurrentCircularBuffer(int capacity)
        {
            if(capacity <= 0)
            {
                throw new ArgumentException($"The capacity specified '{capacity}' is not valid.", nameof(capacity));
            }

            // Setup the queue to the initial capacity using List's underlying implementation.
            _queue = new ConcurrentQueue<TObject>(new List<TObject>(capacity));

            Capacity = capacity;
        }

        public void Enqueue(TObject @object)
        {
            // Enforce the capacity first so the head can be used instead of the entire segment (slow).
            while (_queue.Count + 1 > Capacity)
            {
                if (!_queue.TryDequeue(out _))
                {
                    // Handle error condition however you want to ie throw, return validation object, etc.
                    var ex = new Exception("Concurrent Dequeue operation failed.");
                    ex.Data.Add("EnqueueObject", @object);
                    throw ex;
                }
            }

            // Place the item into the queue
            _queue.Enqueue(@object);
        }

        public TObject Dequeue()
        {
            if(_queue.TryDequeue(out var result))
            {
                return result;
            }

            return default;
        }
    }
}

0

এখানে আরও একটি বাস্তবায়ন যা অন্তর্নিহিত সাম্প্রতিক কুইউউটি যথাসম্ভব ব্যবহার করে একইসাথে ইন্টারফেস সরবরাহের সময় সমকালীন কিউয়ের মাধ্যমে উপলব্ধ।

/// <summary>
/// This is a FIFO concurrent queue that will remove the oldest added items when a given limit is reached.
/// </summary>
/// <typeparam name="TValue"></typeparam>
public class FixedSizedConcurrentQueue<TValue> : IProducerConsumerCollection<TValue>, IReadOnlyCollection<TValue>
{
    private readonly ConcurrentQueue<TValue> _queue;

    private readonly object _syncObject = new object();

    public int LimitSize { get; }

    public FixedSizedConcurrentQueue(int limit)
    {
        _queue = new ConcurrentQueue<TValue>();
        LimitSize = limit;
    }

    public FixedSizedConcurrentQueue(int limit, System.Collections.Generic.IEnumerable<TValue> collection)
    {
        _queue = new ConcurrentQueue<TValue>(collection);
        LimitSize = limit;

    }

    public int Count => _queue.Count;

    bool ICollection.IsSynchronized => ((ICollection) _queue).IsSynchronized;

    object ICollection.SyncRoot => ((ICollection)_queue).SyncRoot; 

    public bool IsEmpty => _queue.IsEmpty;

    // Not supported until .NET Standard 2.1
    //public void Clear() => _queue.Clear();

    public void CopyTo(TValue[] array, int index) => _queue.CopyTo(array, index);

    void ICollection.CopyTo(Array array, int index) => ((ICollection)_queue).CopyTo(array, index);

    public void Enqueue(TValue obj)
    {
        _queue.Enqueue(obj);
        lock( _syncObject )
        {
            while( _queue.Count > LimitSize ) {
                _queue.TryDequeue(out _);
            }
        }
    }

    public IEnumerator<TValue> GetEnumerator() => _queue.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<TValue>)this).GetEnumerator();

    public TValue[] ToArray() => _queue.ToArray();

    public bool TryAdd(TValue item)
    {
        Enqueue(item);
        return true;
    }

    bool IProducerConsumerCollection<TValue>.TryTake(out TValue item) => TryDequeue(out item);

    public bool TryDequeue(out TValue result) => _queue.TryDequeue(out result);

    public bool TryPeek(out TValue result) => _queue.TryPeek(out result);

}

-1

এটি আমার সারিটির সংস্করণ:

public class FixedSizedQueue<T> {
  private object LOCK = new object();
  ConcurrentQueue<T> queue;

  public int MaxSize { get; set; }

  public FixedSizedQueue(int maxSize, IEnumerable<T> items = null) {
     this.MaxSize = maxSize;
     if (items == null) {
        queue = new ConcurrentQueue<T>();
     }
     else {
        queue = new ConcurrentQueue<T>(items);
        EnsureLimitConstraint();
     }
  }

  public void Enqueue(T obj) {
     queue.Enqueue(obj);
     EnsureLimitConstraint();
  }

  private void EnsureLimitConstraint() {
     if (queue.Count > MaxSize) {
        lock (LOCK) {
           T overflow;
           while (queue.Count > MaxSize) {
              queue.TryDequeue(out overflow);
           }
        }
     }
  }


  /// <summary>
  /// returns the current snapshot of the queue
  /// </summary>
  /// <returns></returns>
  public T[] GetSnapshot() {
     return queue.ToArray();
  }
}

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

ডাবল কাউন্ট চেক হ'ল কিছু পরিস্থিতিতে লকটি রোধ করা।


1
কাতারে লক করার জন্য ভোট প্রদান। আপনি যদি একেবারে লক করতে চান তবে একটি রিডার রাইটারলকস্লিম সবচেয়ে ভাল হবে (ধরে নিলে আপনি একটি রাইড লকের চেয়ে প্রায়শই পঠিত লক নেবেন বলে ধারণা করছেন)। গেটস্ন্যাপশটেরও দরকার নেই। আপনি যদি IReadOnly Colલેક્શન <T> প্রয়োগ করেন (যা আপনাকে মূল শব্দার্থবিজ্ঞানের জন্য করা উচিত), টোললিস্ট () একই ফাংশনটি পরিবেশন করবে।
জোশ

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