অ্যাসিঙ্ক্রোনাস ব্লকিং সংগ্রহ <টি> এর মতো কিছু আছে কি?


87

আমি অবিচ্ছিন্নভাবে awaitফলাফলটি BlockingCollection<T>.Take()দেখতে চাই, তাই আমি থ্রেডটি ব্লক করি না। এই জাতীয় কিছু খুঁজছেন:

var item = await blockingCollection.TakeAsync();

আমি জানি আমি এটি করতে পারি:

var item = await Task.Run(() => blockingCollection.Take());

তবে সেই কিন্ডা পুরো ধারণাটিকে মেরে ফেলে, কারণ এর ThreadPoolপরিবর্তে অন্য থ্রেড (এর ) অবরুদ্ধ হয়ে যায়।

কোন বিকল্প আছে?


4
আমি এটি পেলাম না, আপনি যদি await Task.Run(() => blockingCollection.Take())টাস্কটি ব্যবহার করেন তবে অন্য থ্রেডে সঞ্চালিত হবে এবং আপনার ইউআই থ্রেডটি ব্লক হবে না I কথাটি না?
সেলম্যান জেনেজ

8
@ সেলম্যান 22, এটি কোনও ইউআই অ্যাপ নয়। এটি একটি লাইব্রেরি রপ্তানি- Taskভিত্তিক এপিআই। উদাহরণস্বরূপ এটি এএসপি.এনইটি থেকে ব্যবহার করা যেতে পারে। প্রশ্নে কোডটি সেখানে ভাল স্কেল করবে না।
এড়িয়ে চলুন

এর পরেও যদি ConfigureAwaitএটি ব্যবহার করা হত তবে কি সমস্যা হবে Run()? [সম্পাদনা কিছু মনে করবেন না, আপনি এখন যা বলছেন আমি তা দেখতে পাচ্ছি]
মোজো ফিল্টার

উত্তর:


99

আমি জানি যে চারটি বিকল্প আছে।

প্রথমটি হ'ল চ্যানেলগুলি , যা একটি থ্রেডসেফ সারি সরবরাহ করে যা অ্যাসিঙ্ক্রোনাস Readএবং Writeক্রিয়াকলাপগুলিকে সমর্থন করে। চ্যানেলগুলি উচ্চতর অনুকূলিত হয় এবং একটি থ্রেশহোল্ড পৌঁছে গেলে itemsচ্ছিকভাবে কিছু আইটেম বাদ দেওয়ার পক্ষে সমর্থন করে।

পরবর্তীটি টিপিএল ডেটাফ্লোBufferBlock<T> থেকে । আপনি শুধুমাত্র একটি একক ভোক্তা থাকে, তাহলে আপনি ব্যবহার করতে পারেন বা , বা শুধু এটি একটি এর প্রতি সংযোগ আছে । আরও তথ্যের জন্য, আমার ব্লগ দেখুনOutputAvailableAsyncReceiveAsyncActionBlock<T>

শেষ দুটি হ'ল প্রকার যা আমি তৈরি করেছি, আমার অ্যাসিঙ্কেক্স লাইব্রেরিতে উপলভ্য ।

AsyncCollection<T>হয় asyncএর কাছাকাছি-সমতুল্য BlockingCollection<T>, যেমন একটি সহগামী প্রযোজক / ভোক্তা সংগ্রহে মোড়কে করতে সক্ষম ConcurrentQueue<T>বা ConcurrentBag<T>। আপনি TakeAsyncসংগ্রহ থেকে আইটেমগুলি অবিচ্ছিন্নভাবে গ্রাস করতে ব্যবহার করতে পারেন । আরও তথ্যের জন্য, আমার ব্লগ দেখুন

AsyncProducerConsumerQueue<T>আরও বেশি বহনযোগ্য- asyncসামঞ্জস্যপূর্ণ প্রযোজক / গ্রাহক সারি। আপনি DequeueAsyncসারি থেকে আইটেমগুলি অবিচ্ছিন্নভাবে গ্রাস করতে ব্যবহার করতে পারেন । আরও তথ্যের জন্য, আমার ব্লগ দেখুন

এই বিকল্পগুলির শেষ তিনটি সিঙ্ক্রোনাস এবং অ্যাসিনক্রোনাস রাখে এবং গ্রহণের অনুমতি দেয়।



এপিআই ডকুমেন্টেশনে পদ্ধতিটি রয়েছে AsyncCollection.TryTakeAsyncতবে আমি এটি ডাউনলোড করা Nito.AsyncEx.Coordination.dll 5.0.0.0(সর্বশেষ সংস্করণ) খুঁজে পাচ্ছি না । রেফারেন্সিত নিতো.অ্যাসেন্সএক্স.কনকন্টার.ডেল প্যাকেজে উপস্থিত নেই । আমি কী মিস করছি?
থিওডর জৌলিয়াস

@ থিডোরজৌলিয়াস: সেই পদ্ধতিটি v5-এ সরানো হয়েছে। ভি 5 এপি ডক্স এখানে রয়েছে
স্টিফেন ক্লিয়ারি

ধন্যবাদ. দেখে মনে হচ্ছে এটি সংগ্রহটি গণনার সবচেয়ে সহজ এবং নিরাপদ উপায়। while ((result = await collection.TryTakeAsync()).Success) { }। কেন এটি সরানো হয়েছে?
থিওডর জোলিয়াস

4
@ থিডোরজৌলিয়াস: কারণ "চেষ্টা" বলতে বিভিন্ন ব্যক্তির কাছে বিভিন্ন জিনিস বোঝায়। আমি আবার "চেষ্টা" পদ্ধতি যুক্ত করার কথা ভাবছি তবে এটির মূল পদ্ধতির চেয়ে আলাদা শব্দার্থকতা থাকতে পারে। ভবিষ্যতের সংস্করণে অ্যাসিঙ্ক স্ট্রিমগুলিকে সমর্থন করার দিকে লক্ষ্য করা, যা সমর্থিত হলে অবশ্যই ব্যবহারের সর্বোত্তম পদ্ধতি হবে।
স্টিফেন ক্লিয়ারি

21

... বা আপনি এটি করতে পারেন:

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class AsyncQueue<T>
{
    private readonly SemaphoreSlim _sem;
    private readonly ConcurrentQueue<T> _que;

    public AsyncQueue()
    {
        _sem = new SemaphoreSlim(0);
        _que = new ConcurrentQueue<T>();
    }

    public void Enqueue(T item)
    {
        _que.Enqueue(item);
        _sem.Release();
    }

    public void EnqueueRange(IEnumerable<T> source)
    {
        var n = 0;
        foreach (var item in source)
        {
            _que.Enqueue(item);
            n++;
        }
        _sem.Release(n);
    }

    public async Task<T> DequeueAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        for (; ; )
        {
            await _sem.WaitAsync(cancellationToken);

            T item;
            if (_que.TryDequeue(out item))
            {
                return item;
            }
        }
    }
}

সহজ, সম্পূর্ণরূপে কার্যকরী অ্যাসিক্রোনাস ফিফোর সারি।

দ্রষ্টব্য: এর SemaphoreSlim.WaitAsyncআগে .NET 4.5 তে যুক্ত হয়েছিল, এটি এতটা সোজা ছিল না।


4
অসীমের ব্যবহার কী for? যদি সেমফোর প্রকাশিত হয়, সারিতে কমপক্ষে একটি আইটেম শনাক্ত করার আছে, না?
ব্লেন্ডেস্টার

4
@ ব্লেন্ডেস্টার যদি একাধিক গ্রাহককে অবরুদ্ধ করা হয় তবে রেসের শর্ত থাকতে পারে। আমরা নিশ্চিতভাবে জানতে পারি না যে কমপক্ষে দু'জন প্রতিদ্বন্দ্বী ভোক্তা নেই এবং আমরা জানি না যে তারা দু'জন কোনও আইটেমের যোগ্যতা অর্জনের আগে জেগে উঠার ব্যবস্থা করে কিনা। একটি দৌড়ের ইভেন্টে, যদি কেউ প্রজ্ঞাটি পরিচালনা না করে তবে এটি ঘুমাতে ফিরে যাবে এবং অন্য সংকেতের জন্য অপেক্ষা করবে।
জন লেডেগ্রেন

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

4
এটি একটি ব্লক করা সংগ্রহ, এর অর্থশাস্ত্রগুলি, TryDequeueকোনও মান দিয়ে ফিরে আসে, বা মোটেও ফিরে আসে না। প্রযুক্তিগতভাবে, আপনার যদি 1 টির বেশি পাঠক থাকে তবে অন্য পাঠক সম্পূর্ণরূপে জাগ্রত হওয়ার আগে একই পাঠক দুটি (বা আরও) আইটেম গ্রহণ করতে পারেন। একটি সফল WaitAsyncহ'ল একটি সংকেত যা গ্রাহ্য করার জন্য সারিতে আইটেম থাকতে পারে, এটি কোনও গ্যারান্টি নয়।
জন লিডেগ্রেন

ডকস.মাইক্রোসফটকম / জেনোস / ডটনেট / এপি / জি If the value of the CurrentCount property is zero before this method is called, the method also allows releaseCount threads or tasks blocked by a call to the Wait or WaitAsync method to enter the semaphore.থেকে জোনলিডগ্রেন @ সফল কীভাবে WaitAsyncআইটেম সারিতে থাকবে না? এন রিলিজ semaphoreযদি ভেঙে যায় তার চেয়ে এন গ্রাহকদের বেশি জেগে থাকে । তাই না?
আশিষ নেগি

4

এখানে BlockingCollectionপ্রচুর নিখুঁত বৈশিষ্ট্য সহ অপেক্ষারত সমর্থন করে এমন একটি প্রাথমিক বুনিয়াদ বাস্তবায়ন । এটি AsyncEnumerableলাইব্রেরিটি ব্যবহার করে , এটি 8.0-র চেয়ে পুরানো সি # সংস্করণের জন্য অ্যাসিঙ্ক্রোনাস গণনা সম্ভব করে তোলে।

public class AsyncBlockingCollection<T>
{ // Missing features: cancellation, boundedCapacity, TakeAsync
    private Queue<T> _queue = new Queue<T>();
    private SemaphoreSlim _semaphore = new SemaphoreSlim(0);
    private int _consumersCount = 0;
    private bool _isAddingCompleted;

    public void Add(T item)
    {
        lock (_queue)
        {
            if (_isAddingCompleted) throw new InvalidOperationException();
            _queue.Enqueue(item);
        }
        _semaphore.Release();
    }

    public void CompleteAdding()
    {
        lock (_queue)
        {
            if (_isAddingCompleted) return;
            _isAddingCompleted = true;
            if (_consumersCount > 0) _semaphore.Release(_consumersCount);
        }
    }

    public IAsyncEnumerable<T> GetConsumingEnumerable()
    {
        lock (_queue) _consumersCount++;
        return new AsyncEnumerable<T>(async yield =>
        {
            while (true)
            {
                lock (_queue)
                {
                    if (_queue.Count == 0 && _isAddingCompleted) break;
                }
                await _semaphore.WaitAsync();
                bool hasItem;
                T item = default;
                lock (_queue)
                {
                    hasItem = _queue.Count > 0;
                    if (hasItem) item = _queue.Dequeue();
                }
                if (hasItem) await yield.ReturnAsync(item);
            }
        });
    }
}

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

var abc = new AsyncBlockingCollection<int>();
var producer = Task.Run(async () =>
{
    for (int i = 1; i <= 10; i++)
    {
        await Task.Delay(100);
        abc.Add(i);
    }
    abc.CompleteAdding();
});
var consumer = Task.Run(async () =>
{
    await abc.GetConsumingEnumerable().ForEachAsync(async item =>
    {
        await Task.Delay(200);
        await Console.Out.WriteAsync(item + " ");
    });
});
await Task.WhenAll(producer, consumer);

আউটপুট:

1 2 3 4 5 6 7 8 9 10


আপডেট: সি # 8 প্রকাশের সাথে সাথে, অ্যাসিঙ্ক্রোনাস গণনা একটি বিল্ট-ইন ভাষার বৈশিষ্ট্য হয়ে উঠেছে। প্রয়োজনীয় ক্লাসগুলি ( IAsyncEnumerable, IAsyncEnumerator)। নেট কোর 3.0.০ এ এম্বেড করা হয় এবং। নেট ফ্রেমওয়ার্ক ৪.6.১+ ( মাইক্রোসফট.বিসিএল.এসিএনসিআইন্টারনেস ) এর জন্য প্যাকেজ হিসাবে দেওয়া হয় ।

GetConsumingEnumerableনতুন সি # 8 সিনট্যাক্সের বৈশিষ্ট্যযুক্ত এখানে একটি বিকল্প বাস্তবায়ন রয়েছে:

public async IAsyncEnumerable<T> GetConsumingEnumerable()
{
    lock (_queue) _consumersCount++;
    while (true)
    {
        lock (_queue)
        {
            if (_queue.Count == 0 && _isAddingCompleted) break;
        }
        await _semaphore.WaitAsync();
        bool hasItem;
        T item = default;
        lock (_queue)
        {
            hasItem = _queue.Count > 0;
            if (hasItem) item = _queue.Dequeue();
        }
        if (hasItem) yield return item;
    }
}

একই পদ্ধতিতে awaitএবং সহাবস্থানটি নোট করুন yield

ব্যবহারের উদাহরণ (সি # 8):

var consumer = Task.Run(async () =>
{
    await foreach (var item in abc.GetConsumingEnumerable())
    {
        await Task.Delay(200);
        await Console.Out.WriteAsync(item + " ");
    }
});

awaitআগে নোট করুন foreach


4
উত্তরোত্তর হিসাবে, আমি এখন মনে করি যে শ্রেণীর নামটি AsyncBlockingCollectionঅযৌক্তিক। এই দুটি ধারণা হ'ল বিপরীত যেহেতু কিছু একই সাথে অবিচ্ছিন্ন এবং অবরুদ্ধ হতে পারে না!
থিওডর জোলিয়াস

-2

আপনি যদি কিছুটা হ্যাক মনে করেন না, আপনি এই এক্সটেনশনগুলি চেষ্টা করতে পারেন।

public static async Task AddAsync<TEntity>(
    this BlockingCollection<TEntity> Bc, TEntity item, CancellationToken abortCt)
{
    while (true)
    {
        try
        {
            if (Bc.TryAdd(item, 0, abortCt))
                return;
            else
                await Task.Delay(100, abortCt);
        }
        catch (Exception)
        {
            throw;
        }
    }
}

public static async Task<TEntity> TakeAsync<TEntity>(
    this BlockingCollection<TEntity> Bc, CancellationToken abortCt)
{
    while (true)
    {
        try
        {
            TEntity item;

            if (Bc.TryTake(out item, 0, abortCt))
                return item;
            else
                await Task.Delay(100, abortCt);
        }
        catch (Exception)
        {
            throw;
        }
    }
}

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