.NET- এ একটি ব্লক করা ক্যু তৈরি করছে?


163

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

নীচের সমাধানটি হ'ল আমি এখনই যা ব্যবহার করছি এবং আমার প্রশ্ন হ'ল এটি কীভাবে উন্নত করা যায়? ছাত্রলীগে আমার ব্যবহার করা উচিত এমন কোনও বস্তু ইতিমধ্যে সক্ষম করেছে?

internal class BlockingCollection<T> : CollectionBase, IEnumerable
{
    //todo: might be worth changing this into a proper QUEUE

    private AutoResetEvent _FullEvent = new AutoResetEvent(false);

    internal T this[int i]
    {
        get { return (T) List[i]; }
    }

    private int _MaxSize;
    internal int MaxSize
    {
        get { return _MaxSize; }
        set
        {
            _MaxSize = value;
            checkSize();
        }
    }

    internal BlockingCollection(int maxSize)
    {
        MaxSize = maxSize;
    }

    internal void Add(T item)
    {
        Trace.WriteLine(string.Format("BlockingCollection add waiting: {0}", Thread.CurrentThread.ManagedThreadId));

        _FullEvent.WaitOne();

        List.Add(item);

        Trace.WriteLine(string.Format("BlockingCollection item added: {0}", Thread.CurrentThread.ManagedThreadId));

        checkSize();
    }

    internal void Remove(T item)
    {
        lock (List)
        {
            List.Remove(item);
        }

        Trace.WriteLine(string.Format("BlockingCollection item removed: {0}", Thread.CurrentThread.ManagedThreadId));
    }

    protected override void OnRemoveComplete(int index, object value)
    {
        checkSize();
        base.OnRemoveComplete(index, value);
    }

    internal new IEnumerator GetEnumerator()
    {
        return List.GetEnumerator();
    }

    private void checkSize()
    {
        if (Count < MaxSize)
        {
            Trace.WriteLine(string.Format("BlockingCollection FullEvent set: {0}", Thread.CurrentThread.ManagedThreadId));
            _FullEvent.Set();
        }
        else
        {
            Trace.WriteLine(string.Format("BlockingCollection FullEvent reset: {0}", Thread.CurrentThread.ManagedThreadId));
            _FullEvent.Reset();
        }
    }
}

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

আমি মনে করি মনিটরের সম্পর্কে জানতে এখনও ভাল ধারণা।
thewpfguy

1
@Thepfguy এর সাথে সম্মত হন। আপনি পর্দার পিছনে বেসিক লকিং প্রক্রিয়া বুঝতে চাইবেন। এছাড়াও লক্ষনীয় যে সিস্টেমস.কলেশনস.কন্ট্রেন্টটি এপ্রিল ২০১০ পর্যন্ত উপস্থিত ছিল না এবং তারপরে কেবল ভিজ্যুয়াল স্টুডিও 2010 এবং তারপরে। নিশ্চিতভাবেই না VS2008 হোল্ড আউট জন্য একটি বিকল্প ...
ভিক

আপনি যদি এখন এটি পড়ছেন তবে System.Threading.NET কোর এবং .NET স্ট্যান্ডার্ডের জন্য এটির একাধিক লেখক / মাল্টি-রিডার, সীমাবদ্ধ, allyচ্ছিকভাবে-ব্লকিং প্রয়োগকরণের জন্য চ্যানেলগুলি দেখুন।
মার্ক রেন্ডেল

উত্তর:


200

এটি দেখতে খুব অনিরাপদ দেখাচ্ছে (খুব সামান্য সিঙ্ক্রোনাইজেশন); যেমন কিছু সম্পর্কে:

class SizeQueue<T>
{
    private readonly Queue<T> queue = new Queue<T>();
    private readonly int maxSize;
    public SizeQueue(int maxSize) { this.maxSize = maxSize; }

    public void Enqueue(T item)
    {
        lock (queue)
        {
            while (queue.Count >= maxSize)
            {
                Monitor.Wait(queue);
            }
            queue.Enqueue(item);
            if (queue.Count == 1)
            {
                // wake up any blocked dequeue
                Monitor.PulseAll(queue);
            }
        }
    }
    public T Dequeue()
    {
        lock (queue)
        {
            while (queue.Count == 0)
            {
                Monitor.Wait(queue);
            }
            T item = queue.Dequeue();
            if (queue.Count == maxSize - 1)
            {
                // wake up any blocked enqueue
                Monitor.PulseAll(queue);
            }
            return item;
        }
    }
}

(সম্পাদনা)

বাস্তবে, আপনি সারিটি বন্ধ করার একটি উপায় চাইবেন যাতে পাঠকরা পরিষ্কারভাবে প্রস্থান করতে শুরু করে - সম্ভবত একটি বুল পতাকার মতো কিছু - যদি সেট করা থাকে তবে খালি সারিটি কেবল ফিরে আসে (অবরুদ্ধ করার পরিবর্তে):

bool closing;
public void Close()
{
    lock(queue)
    {
        closing = true;
        Monitor.PulseAll(queue);
    }
}
public bool TryDequeue(out T value)
{
    lock (queue)
    {
        while (queue.Count == 0)
        {
            if (closing)
            {
                value = default(T);
                return false;
            }
            Monitor.Wait(queue);
        }
        value = queue.Dequeue();
        if (queue.Count == maxSize - 1)
        {
            // wake up any blocked enqueue
            Monitor.PulseAll(queue);
        }
        return true;
    }
}

1
ওয়েটঅ্যানির অপেক্ষার পরিবর্তন এবং নির্মাণের জন্য একটি টার্মিনেট ওয়েটহ্যান্ডলে যাওয়ার বিষয়ে কীভাবে ...
স্যাম

1
@ মার্ক- একটি অপ্টিমাইজেশন, আপনি যদি সারিটি সর্বদা সক্ষমতা পৌঁছানোর প্রত্যাশা করছিলেন তবে সর্বাধিক আকারের মানটি কাতারের <<<> নির্মাণকারীর কাছে পৌঁছে দেওয়া ছিল। আপনি এটির জন্য আপনার ক্লাসে অন্য একজন কনস্ট্রাক্টর যুক্ত করতে পারেন।
রিচার্ডড

3
সাইজকিউ কেন, ফিক্সডসাইজ কিউ নয় কেন?
mindless.panda

4
@ লাস - এটি লক (গুলি) চলাকালীন প্রকাশ করে Wait, যাতে অন্যান্য থ্রেড এটি অর্জন করতে পারে। এটি ঘুম থেকে ওঠার পরে লকটি পুনরায় দাবি করে।
মার্ক Gravell

1
খুব ভাল, যেমনটি আমি বলেছিলাম, এখানে আমি কিছু পাচ্ছিলাম না :) এটি নিশ্চিতভাবেই আমার কিছু থ্রেড-কোডটি পুনরায় দেখাতে চাইছে ....
লাসে ভি। কার্লসেন

59

.Net 4 ব্লকিংকলেকশন ব্যবহার করুন, টান () ব্যবহারটি সন্ধান করার জন্য অ্যাড () ব্যবহার করুন en এটি অভ্যন্তরীণভাবে অবরুদ্ধকরণ নয় এমন সমকালীন কিউউ ব্যবহার করে। এখানে আরও তথ্য দ্রুত এবং সেরা প্রযোজক / গ্রাহক সারি কৌশল ব্লকিংকলেকশন বনাম সমবর্তী সারিতে


14

"এটি কীভাবে উন্নত করা যায়?"

ঠিক আছে, আপনার নিজের শ্রেণীর প্রতিটি পদ্ধতিটি দেখার প্রয়োজন এবং যদি অন্য থ্রেড একই সাথে সেই পদ্ধতি বা অন্য কোনও পদ্ধতি কল করে তবে কী হবে তা বিবেচনা করা উচিত। উদাহরণস্বরূপ, আপনি সরান পদ্ধতিতে একটি লক রেখেছেন, তবে অ্যাড পদ্ধতিতে নয়। যদি একটি থ্রেড একই সাথে অন্য থ্রেড সরানো হয় তবে কী ঘটে? খারাপ জিনিসগুলো.

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

থাম্বের একটি ভাল নিয়ম হ'ল ক্লাসে পদ্ধতির সংখ্যা কেটে নিখুঁত ন্যূনতম পর্যন্ত ডান পেতে এটি সহজতর করা।

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

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

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

আপডেট: মার্কের উত্তরটি আসলে এই সমস্ত পরামর্শ কার্যকর করে! :) তবে আমি এটি এখানে রেখে যাব কারণ এটির সংস্করণটি কেন এমন উন্নতি তা বোঝা সহায়ক হতে পারে।


12

আপনি ব্যবহার করতে পারেন BlockingCollection এবং ConcurrentQueue System.Collections.Concurrent নামস্থান মধ্যে

 public class ProducerConsumerQueue<T> : BlockingCollection<T>
{
    /// <summary>
    /// Initializes a new instance of the ProducerConsumerQueue, Use Add and TryAdd for Enqueue and TryEnqueue and Take and TryTake for Dequeue and TryDequeue functionality
    /// </summary>
    public ProducerConsumerQueue()  
        : base(new ConcurrentQueue<T>())
    {
    }

  /// <summary>
  /// Initializes a new instance of the ProducerConsumerQueue, Use Add and TryAdd for Enqueue and TryEnqueue and Take and TryTake for Dequeue and TryDequeue functionality
  /// </summary>
  /// <param name="maxSize"></param>
    public ProducerConsumerQueue(int maxSize)
        : base(new ConcurrentQueue<T>(), maxSize)
    {
    }



}

3
ব্লকিংকোলেকশনটি কাতারে ডিফল্ট। সুতরাং, আমি এটি প্রয়োজনীয় বলে মনে করি না।
কার্টিস হোয়াইট

ব্লকিংকলেশন কি কিউয়ের মতো অর্ডার সংরক্ষণ করে?
joelc

হ্যাঁ, যখন এটি কনক্র্যান্টকুইউ দিয়ে শুরু করা হয়
অ্যান্ড্রেয়াস

6

আমি কেবল প্রতিক্রিয়াশীল এক্সটেনশানগুলি ব্যবহার করে এটি ছিটকেছি এবং এই প্রশ্নের মনে পড়ে:

public class BlockingQueue<T>
{
    private readonly Subject<T> _queue;
    private readonly IEnumerator<T> _enumerator;
    private readonly object _sync = new object();

    public BlockingQueue()
    {
        _queue = new Subject<T>();
        _enumerator = _queue.GetEnumerator();
    }

    public void Enqueue(T item)
    {
        lock (_sync)
        {
            _queue.OnNext(item);
        }
    }

    public T Dequeue()
    {
        _enumerator.MoveNext();
        return _enumerator.Current;
    }
}

অগত্যা সম্পূর্ণ নিরাপদ নয়, তবে খুব সহজ।


বিষয় <t> কী? এর নেমস্পেসের জন্য আমার কোনও রেজলভার নেই।
জেরম

এটি প্রতিক্রিয়াশীল এক্সটেনশনের অংশ।
মার্ক রেন্ডেল

কোনও উত্তর নয়। এটি প্রশ্নের কোনও উত্তর দেয় না।
মাখদুমী

5

এটিই আমি থ্রেড সেফ বাউন্ডড ব্লকিং সারিটির জন্য এসেছি।

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

public class BlockingBuffer<T>
{
    private Object t_lock;
    private Semaphore sema_NotEmpty;
    private Semaphore sema_NotFull;
    private T[] buf;

    private int getFromIndex;
    private int putToIndex;
    private int size;
    private int numItems;

    public BlockingBuffer(int Capacity)
    {
        if (Capacity <= 0)
            throw new ArgumentOutOfRangeException("Capacity must be larger than 0");

        t_lock = new Object();
        buf = new T[Capacity];
        sema_NotEmpty = new Semaphore(0, Capacity);
        sema_NotFull = new Semaphore(Capacity, Capacity);
        getFromIndex = 0;
        putToIndex = 0;
        size = Capacity;
        numItems = 0;
    }

    public void put(T item)
    {
        sema_NotFull.WaitOne();
        lock (t_lock)
        {
            while (numItems == size)
            {
                Monitor.Pulse(t_lock);
                Monitor.Wait(t_lock);
            }

            buf[putToIndex++] = item;

            if (putToIndex == size)
                putToIndex = 0;

            numItems++;

            Monitor.Pulse(t_lock);

        }
        sema_NotEmpty.Release();


    }

    public T take()
    {
        T item;

        sema_NotEmpty.WaitOne();
        lock (t_lock)
        {

            while (numItems == 0)
            {
                Monitor.Pulse(t_lock);
                Monitor.Wait(t_lock);
            }

            item = buf[getFromIndex++];

            if (getFromIndex == size)
                getFromIndex = 0;

            numItems--;

            Monitor.Pulse(t_lock);

        }
        sema_NotFull.Release();

        return item;
    }
}

আমি এই ক্লাসটি ইনস্ট্যান্ট করব কী করে এই লাইব্রেরিটি ব্যবহার করে আমি কীভাবে কিছু থ্রেড ফাংশন সারি করবো তার কিছু কোড নমুনা সরবরাহ করতে পারি?
জেরম

এই প্রশ্ন / প্রতিক্রিয়াটি কিছুটা তারিখের। সারি সমর্থন অবরুদ্ধ করার জন্য আপনার সিস্টেম.কলেশন.কন্ট্রেন্ট নেমস্পেসের দিকে নজর দেওয়া উচিত।
কেভিন

2

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

আশা করি এইটি কাজ করবে.


আমি জানি যে এটি পুরানো, তবে আমার মন্তব্যটি এসও-তে আগতদের জন্য, কারণ ওপি ইতিমধ্যে এটি আজই জানেন। এটি কোনও উত্তর নয়, এটি একটি মন্তব্য করা উচিত ছিল।
জন দেমেট্রিও

0

ভাল, আপনি System.Threading.Semaphoreক্লাস তাকান হতে পারে । তা ছাড়া - না, আপনাকে এটি নিজেই তৈরি করতে হবে। এএফআইকে তেমন কোনও অন্তর্নির্মিত সংগ্রহ নেই।


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

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

আপনার এখনকার চেয়ে এটি আলাদা নয়। আরও সহজ আইএমএইচও।
ভিলেক্স-

আপনি কি আমাকে এই কাজ করে একটি উদাহরণ দিতে পারেন? এই দৃশ্যের প্রয়োজন মতো গতিময়ভাবে সেমফোরের আকারটি কীভাবে সমন্বয় করা যায় তা আমি দেখিনি। যেহেতু আপনার সারিটি পূর্ণ হলেই আপনাকে সমস্ত সংস্থানগুলি ব্লক করতে সক্ষম হতে হবে।
এরিক শুনোভার

আহ, আকার পরিবর্তন! তাত্ক্ষণিক কথা বললে না কেন? ঠিক আছে, তাহলে একটি সেমফোর আপনার জন্য নয়। এই পদ্ধতির সাথে শুভকামনা!
ভিলাক্স-

-1

যদি আপনি সর্বাধিক থ্রুপুট চান, একাধিক পাঠককে পড়তে এবং কেবল একজন লেখককে লেখার অনুমতি দেয়, বিসিএলের কাছে রিডার রাইটারলকস্লিম নামে কিছু আছে যা আপনার কোডটি স্লিম করতে সহায়তা করবে ...


আমি চাই যে সারিটি পূর্ণ থাকলেও কেউ লিখতে সক্ষম হবে না।
এরিক শুনোভার

সুতরাং আপনি এটি একটি লক সঙ্গে একত্রিত। এখানে কিছু খুব ভাল উদাহরণ দেওয়া আছে আলবাহারী. com
threading/

3
সারি / প্রাত্যহিকতার সাথে প্রত্যেকেই একজন লেখক ... একটি এক্সক্লুসিভ লক সম্ভবত আরও বাস্তববাদী হবে
মার্ক গ্র্যাভেল

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