অ্যাসিনক্রোনাস লাম্বদা সহ সমান্তরাল ফোরচ


138

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

আমি যদি সমান্তরাল লুপের ল্যাম্বদার মধ্যে সি # তে অ্যাসিঙ্ক চিহ্নিত পদ্ধতিতে কল করতে চাই তবে সমস্যা দেখা দেয়। উদাহরণ স্বরূপ:

var bag = new ConcurrentBag<object>();
Parallel.ForEach(myCollection, async item =>
{
  // some pre stuff
  var response = await GetData(item);
  bag.Add(response);
  // some post stuff
}
var count = bag.Count;

সমস্যাটি গণনা 0 হওয়ার সাথে দেখা দেয় কারণ তৈরি করা সমস্ত থ্রেড কার্যকরভাবে কেবল ব্যাকগ্রাউন্ড থ্রেড এবং Parallel.ForEachকল সম্পূর্ণ হওয়ার জন্য অপেক্ষা করে না। যদি আমি অ্যাসিঙ্ক কীওয়ার্ডটি সরিয়ে ফেলি, তবে পদ্ধতিটি এমন দেখাচ্ছে:

var bag = new ConcurrentBag<object>();
Parallel.ForEach(myCollection, item =>
{
  // some pre stuff
  var responseTask = await GetData(item);
  responseTask.Wait();
  var response = responseTask.Result;
  bag.Add(response);
  // some post stuff
}
var count = bag.Count;

এটি কাজ করে, তবে এটি প্রতীক্ষিত চতুরতা সম্পূর্ণরূপে অক্ষম করে এবং আমাকে কিছু ম্যানুয়াল ব্যতিক্রম হ্যান্ডলিং করতে হবে .. (ব্রেভিটির জন্য মুছে ফেলা হয়েছে)।

আমি কীভাবে একটি Parallel.ForEachলুপ বাস্তবায়ন করতে পারি , যা ল্যাম্বডায় অপেক্ষার কীওয়ার্ডটি ব্যবহার করে? এটা কি সম্ভব?

প্যারালাল.ফোরএচ পদ্ধতির প্রোটোটাইপটি Action<T>প্যারামিটার হিসাবে গ্রহণ করে তবে আমি এটি চাই আমার অ্যাসিনক্রোনাস ল্যাম্বদার জন্য অপেক্ষা করা।


1
আমি ধরে নিচ্ছি যে আপনি আপনার দ্বিতীয় কোড ব্লক awaitথেকে সরিয়ে ফেলতে চেয়েছিলেন await GetData(item)কারণ এটি যেমন একটি সংকলন ত্রুটি তৈরি করবে।
জোশ এম

উত্তর:


186

আপনি যদি কেবল সাধারণ সমান্তরালতা চান তবে আপনি এটি করতে পারেন:

var bag = new ConcurrentBag<object>();
var tasks = myCollection.Select(async item =>
{
  // some pre stuff
  var response = await GetData(item);
  bag.Add(response);
  // some post stuff
});
await Task.WhenAll(tasks);
var count = bag.Count;

আপনার যদি আরও জটিল কিছু প্রয়োজন হয় তবে স্টিফেন টুবের ForEachAsyncপোস্ট দেখুন


46
সম্ভবত একটি থ্রোটলিং ব্যবস্থা প্রয়োজন। এটি তাত্ক্ষণিকভাবে অনেকগুলি কার্য তৈরি করবে যতগুলি আইটেম রয়েছে যা 10 কে নেটওয়ার্ক অনুরোধ এবং এর মধ্যে শেষ হতে পারে।
usr ডিরেক্টরির

10
@ ইউএস স্টিফেন টুবের নিবন্ধের শেষ উদাহরণটি এতে সম্বোধন করে।
সুইভ

@ এসভিক আমি শেষের নমুনাটি নিয়ে অবাক হয়েছি। আমার কাছে মনে হচ্ছে যে এটি আমার কাছে আরও কাজ তৈরি করতে কেবলমাত্র একটি প্রচুর টাস্ককে ব্যাচ করে তবে তারা সকলেই শুরু করে।
লুক পুপলেট

2
@ লুক্কুপ্লেট এটি dopকার্য তৈরি করে এবং তাদের প্রত্যেকেরই ধারাবাহিকতায় ইনপুট সংগ্রহের কিছু উপসেট প্রক্রিয়া করে।
সুইভ

4
@Afshin_Zavvar: যদি আপনি কল Task.Runছাড়া awaitফলে ing তারপর থ্রেড পুল সম্মুখের আগুন-এবং-ভুলবেন কাজ নিক্ষেপ যে। এটি প্রায় সবসময়ই একটি ভুল।
স্টিফেন ক্লিয়ারি

74

আপনি AsyncEnumerator নিউগেট প্যাকেজParallelForEachAsync থেকে এক্সটেনশন পদ্ধতিটি ব্যবহার করতে পারেন :

using Dasync.Collections;

var bag = new ConcurrentBag<object>();
await myCollection.ParallelForEachAsync(async item =>
{
  // some pre stuff
  var response = await GetData(item);
  bag.Add(response);
  // some post stuff
}, maxDegreeOfParallelism: 10);
var count = bag.Count;

1
এটা কি আপনার প্যাকেজ? আমি আপনাকে এখন কয়েক জায়গায় পোস্ট করতে দেখেছি? : ডি ওহ অপেক্ষা করুন .. আপনার নাম প্যাকেজে রয়েছে: ডি +1
পিয়োটার কুলা

17
@ পিপমকিন, হ্যাঁ, এটি আমার। আমি এই সমস্যাটি বারবার দেখেছি, তাই এটি সহজতম সমাধানে সমাধান করার এবং অন্যদেরও লড়াই থেকে মুক্ত করার সিদ্ধান্ত নিয়েছে :)
সার্জ সেমেনভ

ধন্যবাদ .. এটি অবশ্যই বোধগম্য এবং বড় সময় আমাকে সাহায্য করেছে!
পাইওটার কুলা

2
আপনার একটি টাইপ রয়েছে: maxDegreeOfParallelism>maxDegreeOfParalellism
শিরান আতঙ্ক

3
সঠিক বানানটি অবশ্যই ম্যাক্সডেগ্রিঅফপ্যারালাইজলিজম, তবে @ শিরনডরারের মন্তব্যে কিছু আছে - আপনার প্যাকেজে আপনি ভেরিয়েবল ম্যাক্সডেগ্রিঅফপ্যারালিজম ভুল করে বলেছিলেন (এবং তাই এটি পরিবর্তন না হওয়া পর্যন্ত আপনার উদ্ধৃত কোডটি সংকলন করবে না)
বার্নটোকোড

17

আপনার সাথে SemaphoreSlimসমান্তরালতা নিয়ন্ত্রণ অর্জন করতে পারেন।

var bag = new ConcurrentBag<object>();
var maxParallel = 20;
var throttler = new SemaphoreSlim(initialCount: maxParallel);
var tasks = myCollection.Select(async item =>
{
  try
  {
     await throttler.WaitAsync();
     var response = await GetData(item);
     bag.Add(response);
  }
  finally
  {
     throttler.Release();
  }
});
await Task.WhenAll(tasks);
var count = bag.Count;

3

প্যারালাল ফরএচ অ্যাসিঙ্কের আমার হালকা ওজনের বাস্তবায়ন।

বৈশিষ্ট্য:

  1. থ্রোটলিং (সমান্তরালনের সর্বোচ্চ ডিগ্রি)।
  2. ব্যতিক্রম হ্যান্ডলিং (সমষ্টি ব্যতিক্রম সমাপ্তিতে নিক্ষেপ করা হবে)।
  3. স্মৃতিশক্তি দক্ষ (কার্যগুলির তালিকাগুলি সংরক্ষণ করার প্রয়োজন নেই)।

public static class AsyncEx
{
    public static async Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> asyncAction, int maxDegreeOfParallelism = 10)
    {
        var semaphoreSlim = new SemaphoreSlim(maxDegreeOfParallelism);
        var tcs = new TaskCompletionSource<object>();
        var exceptions = new ConcurrentBag<Exception>();
        bool addingCompleted = false;

        foreach (T item in source)
        {
            await semaphoreSlim.WaitAsync();
            asyncAction(item).ContinueWith(t =>
            {
                semaphoreSlim.Release();

                if (t.Exception != null)
                {
                    exceptions.Add(t.Exception);
                }

                if (Volatile.Read(ref addingCompleted) && semaphoreSlim.CurrentCount == maxDegreeOfParallelism)
                {
                    tcs.SetResult(null);
                }
            });
        }

        Volatile.Write(ref addingCompleted, true);
        await tcs.Task;
        if (exceptions.Count > 0)
        {
            throw new AggregateException(exceptions);
        }
    }
}

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

await Enumerable.Range(1, 10000).ParallelForEachAsync(async (i) =>
{
    var data = await GetData(i);
}, maxDegreeOfParallelism: 100);

2

আমি এর জন্য একটি এক্সটেনশন পদ্ধতি তৈরি করেছি যা সেমফোরস্লিম ব্যবহার করে এবং সর্বাধিক ডিগ্রি সমান্তরালতা সেট করতে দেয়

    /// <summary>
    /// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T">Type of IEnumerable</typeparam>
    /// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
    /// <param name="action">an async <see cref="Action" /> to execute</param>
    /// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
    /// Must be grater than 0</param>
    /// <returns>A Task representing an async operation</returns>
    /// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
    public static async Task ForEachAsyncConcurrent<T>(
        this IEnumerable<T> enumerable,
        Func<T, Task> action,
        int? maxDegreeOfParallelism = null)
    {
        if (maxDegreeOfParallelism.HasValue)
        {
            using (var semaphoreSlim = new SemaphoreSlim(
                maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
            {
                var tasksWithThrottler = new List<Task>();

                foreach (var item in enumerable)
                {
                    // Increment the number of currently running tasks and wait if they are more than limit.
                    await semaphoreSlim.WaitAsync();

                    tasksWithThrottler.Add(Task.Run(async () =>
                    {
                        await action(item).ContinueWith(res =>
                        {
                            // action is completed, so decrement the number of currently running tasks
                            semaphoreSlim.Release();
                        });
                    }));
                }

                // Wait for all tasks to complete.
                await Task.WhenAll(tasksWithThrottler.ToArray());
            }
        }
        else
        {
            await Task.WhenAll(enumerable.Select(item => action(item)));
        }
    }

নমুনা ব্যবহার:

await enumerable.ForEachAsyncConcurrent(
    async item =>
    {
        await SomeAsyncMethod(item);
    },
    5);

'ব্যবহার' সাহায্য করবে না। ফরচ লুপ অনির্দিষ্টকালের জন্য সেমফোনের জন্য অপেক্ষা করবে। কেবলমাত্র এই সহজ কোডটি চেষ্টা করুন যা সমস্যার পুনঃপ্রবর্তন করে: এনুমিউরেবলের অপেক্ষা করুন an 2);
নিকোলে.নেইকিয়েনকো

@ নিকোলে.অন্যকিয়েনকো আপনি ঠিক # 2 এর সম্পর্কে ঠিক বলেছেন। এই মেমরির সমস্যাটি কার্যবিবরণী ট্রট্রোলটার যোগ করে সমাধান করা যেতে পারে। রিমোভাল (x => x.IsCompleted);
জিজ্ঞাসা করুন

1
আমি আমার কোডটিতে এটি চেষ্টা করেছি এবং যদি আমি ম্যাক্সডেগ্রিঅফপ্যারালালিজম কোড ডেডলকগুলি বাতিল করে না। : এখানে আপনি প্রজনন করার সমস্ত কোড দেখতে পারেন stackoverflow.com/questions/58793118/...
মাসিমো Savazzi
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.