সমান্তরালভাবে নেস্টিং অপেক্ষা করছে For


183

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

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

var ids = new List<string>() { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
var customers = new  System.Collections.Concurrent.BlockingCollection<Customer>();

Parallel.ForEach(ids, async i =>
{
    ICustomerRepo repo = new CustomerRepo();
    var cust = await repo.GetCustomer(i);
    customers.Add(cust);
});

foreach ( var customer in customers )
{
    Console.WriteLine(customer.ID);
}

Console.ReadKey();

উত্তর:


171

পেছনের পুরো ধারণাটি Parallel.ForEach()হ'ল আপনার থ্রেডগুলির একটি সেট রয়েছে এবং প্রতিটি থ্রেড সংগ্রহের অংশ প্রসেস করে। যেমন আপনি লক্ষ্য করেছেন, এটি async- এর সাথে কাজ করে না await, যেখানে আপনি অ্যাসিঙ্ক কলটির সময়কালের জন্য থ্রেডটি প্রকাশ করতে চান।

আপনি "ফিক্স" পারে যে অবরুদ্ধ করে ForEach()থ্রেড, কিন্তু যে নষ্ট সমগ্র বিন্দু async- await

আপনি যা করতে পারলেন তা হল পরিবর্তে টিপিএল ডেটাফ্লো ব্যবহার করা Parallel.ForEach(), যা অ্যাসিঙ্ক্রোনাসকে Taskভাল সমর্থন করে ।

বিশেষত, আপনার কোডটি এমন একটি ব্যবহার করে লেখা যেতে পারে যা TransformBlockপ্রতিটি আইডিকে ল্যাম্বডা Customerব্যবহার করে রূপান্তর করে async। এই ব্লকটি সমান্তরালভাবে সম্পাদন করতে কনফিগার করা যেতে পারে। আপনি সেই ব্লকটিকে এমন একটিতে লিঙ্ক করবেন যা কনসোলে ActionBlockপ্রতিটি লিখেছে Customer। আপনি ব্লক নেটওয়ার্ক সেট আপ করার পরে, আপনি Post()প্রতিটি আইডি করতে পারেন TransformBlock

কোডে:

var ids = new List<string> { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };

var getCustomerBlock = new TransformBlock<string, Customer>(
    async i =>
    {
        ICustomerRepo repo = new CustomerRepo();
        return await repo.GetCustomer(i);
    }, new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded
    });
var writeCustomerBlock = new ActionBlock<Customer>(c => Console.WriteLine(c.ID));
getCustomerBlock.LinkTo(
    writeCustomerBlock, new DataflowLinkOptions
    {
        PropagateCompletion = true
    });

foreach (var id in ids)
    getCustomerBlock.Post(id);

getCustomerBlock.Complete();
writeCustomerBlock.Completion.Wait();

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

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


2
অ্যাসিঙ্ক, প্রতিক্রিয়াশীল এক্সটেনশনগুলি, টিপিএল এবং টিপিএল ডেটাফ্লো - vantsuyoshi.wordpress.com/2012/01/05/… সম্পর্কে একটি সংক্ষিপ্ত সংক্ষিপ্ত বিবরণ আমার জন্য যাদের কিছুটা স্পষ্টতার প্রয়োজন হতে পারে।
নরম্যান এইচ

1
আমি নিশ্চিত যে এই উত্তরটি প্রক্রিয়াটিকে সমান্তরাল করে না। আমি বিশ্বাস করি আপনার একটি সমান্তরাল করা দরকার For আইডির উপরে প্রতিটি করুন এবং সেগুলি কাস্টমোরব্লকটিতে পোস্ট করুন। আমি এই পরামর্শটি পরীক্ষা করার সময় কমপক্ষে এটিই পেয়েছিলাম।
জেসনলাইন্ড

4
@ জেসনলিন্ড এটি সত্যিই করে। ব্যবহার Parallel.ForEach()করার জন্য Post()সমান্তরাল আইটেম কোনো বাস্তব প্রভাব আছে করা উচিত নয়।
সোভিক

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

2
যার অর্থ আমি উদাহরণস্বরূপ ট্রান্সফর্মব্লকটিতে যেমন করি তেমনি অ্যাকশনব্লকটিতে ম্যাক্সডগ্রিঅফপ্যাটারালিজম নির্দিষ্ট করে বলতে চাই
জেসনলাইন্ড

125

এসভিকের উত্তরটি (যথারীতি) দুর্দান্ত।

যাইহোক, যখন আপনার কাছে হস্তান্তর করতে আসলে প্রচুর পরিমাণে ডেটা থাকে তখন আমি ডেটাফ্লোটিকে আরও দরকারী বলে মনে করি। অথবা যখন আপনার একটি asyncসামঞ্জস্যপূর্ণ সারি প্রয়োজন ।

আপনার ক্ষেত্রে, একটি সহজ সমাধান হ'ল asyncস্টাইল সমান্তরালতাটি ব্যবহার করা :

var ids = new List<string>() { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };

var customerTasks = ids.Select(i =>
  {
    ICustomerRepo repo = new CustomerRepo();
    return repo.GetCustomer(i);
  });
var customers = await Task.WhenAll(customerTasks);

foreach (var customer in customers)
{
  Console.WriteLine(customer.ID);
}

Console.ReadKey();

13
আপনি যদি ম্যানুয়ালি প্যারালালিজম সীমাবদ্ধ করতে চান (যা আপনি সম্ভবত এই ক্ষেত্রে করেন) তবে এটি এভাবে করা আরও জটিল হবে।
সুইভ

1
তবে আপনি ঠিক বলেছেন যে ডেটাফ্লো বেশ জটিল হতে পারে (উদাহরণস্বরূপ যখন তুলনা করা হয় Parallel.ForEach())। তবে আমি মনে করি asyncসংগ্রহের সাথে প্রায় কোনও কাজ করার জন্য এটি বর্তমানে সেরা বিকল্প ।
svick

1
@ জেমসম্যানিং কীভাবে ParallelOptionsসাহায্য করবে? এটি কেবলমাত্র প্রযোজ্য Parallel.For/ForEach/Invoke, এটি ওপি হিসাবে প্রতিষ্ঠিত হিসাবে এখানে কোনও কাজে আসে না।
ওহাদ স্নাইডার

1
@ স্টেফেনক্রিয়ারি GetCustomerপদ্ধতিটি যদি কোনও ফিরে আসে Task<T>তবে একজনকে Select(async i => { await repo.GetCustomer(i);});কী ব্যবহার করা উচিত ?
শীজু

5
@ ব্যাটমাসি: Parallel.ForEachসমর্থন করে না async
স্টিফেন ক্লিয়ারি

79

ডেভফ্লোকে সুইভের প্রস্তাবিত হিসাবে ব্যবহার করা অত্যধিক মেশিন হতে পারে এবং স্টিফেনের উত্তর অপারেশনের একযোগে নিয়ন্ত্রণের উপায় সরবরাহ করে না। তবে, এটি বরং সহজভাবে অর্জন করা যেতে পারে:

public static async Task RunWithMaxDegreeOfConcurrency<T>(
     int maxDegreeOfConcurrency, IEnumerable<T> collection, Func<T, Task> taskFactory)
{
    var activeTasks = new List<Task>(maxDegreeOfConcurrency);
    foreach (var task in collection.Select(taskFactory))
    {
        activeTasks.Add(task);
        if (activeTasks.Count == maxDegreeOfConcurrency)
        {
            await Task.WhenAny(activeTasks.ToArray());
            //observe exceptions here
            activeTasks.RemoveAll(t => t.IsCompleted); 
        }
    }
    await Task.WhenAll(activeTasks.ToArray()).ContinueWith(t => 
    {
        //observe exceptions in a manner consistent with the above   
    });
}

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

RunWithMaxDegreeOfConcurrency(10, ids, async i =>
{
    ICustomerRepo repo = new CustomerRepo();
    var cust = await repo.GetCustomer(i);
    customers.Add(cust);
});

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

public static Task ForEachAsync<T>(
      this IEnumerable<T> source, int dop, Func<T, Task> body) 
{ 
    return Task.WhenAll( 
        from partition in Partitioner.Create(source).GetPartitions(dop) 
        select Task.Run(async delegate { 
            using (partition) 
                while (partition.MoveNext()) 
                    await body(partition.Current).ContinueWith(t => 
                          {
                              //observe exceptions
                          });

        })); 
}

1
@ রিচার্ডপিয়ের আসলে এই অতিরিক্ত ব্যবহারের অংশটিকে Partitioner.Createবিভাজনে ভাগ করে নিচ্ছে , যা বিভিন্ন কার্যগুলিতে উপাদানগুলিকে গতিশীলভাবে সরবরাহ করে যাতে আপনার বর্ণিত দৃশ্যধারণটি ঘটবে না। আরও মনে রাখবেন যে স্থির (পূর্ব নির্ধারিত) পার্টিশন কিছু ক্ষেত্রে কম ওভারহেডের কারণে (বিশেষত সিঙ্ক্রোনাইজেশন) দ্রুত হতে পারে। আরও তথ্যের জন্য দেখুন: এমএসডিএন.মাইক্রোসফটকম /en-us/library/dd997411(v=vs.110).aspx
ওহাদ স্নাইডার

1
@ ওহাদস্কিনিডার // এ ব্যতিক্রমগুলি পর্যবেক্ষণ করুন, যদি এটি একটি ব্যতিক্রম ছুঁড়ে দেয়, তবে কি এটি কলারের কাছে বুদবুদ হবে? উদাহরণস্বরূপ, আমি যদি পুরো গণনাকারীটিকে প্রক্রিয়াজাতকরণ বন্ধ করতে / ব্যর্থ করতে চাই তবে এর কোনও অংশ ব্যর্থ হয়?
টেরি

3
@ টেরি এটি কলকারীদের কাছে বুদবুদ হয়ে উঠবে এই অর্থে যে সর্বাধিক-সর্বাধিক কার্য (যার দ্বারা তৈরি করা হয়েছে Task.WhenAll) ব্যতিক্রম (একটি এর ভিতরে) থাকবে AggregateExceptionএবং ফলস্বরূপ যদি কলার ব্যবহৃত হয় await, কল সাইটে একটি ব্যতিক্রম ছুঁড়ে ফেলা হবে। যাইহোক, Task.WhenAllএখনও সমস্ত কাজ শেষ হওয়ার জন্য অপেক্ষা GetPartitionsকরবে এবং যখন partition.MoveNextআরও কোনও উপাদান প্রক্রিয়া না করা অবধি ডাকা হয় তখন ডায়নামিকভাবে উপাদানগুলি বরাদ্দ করা হবে । এর অর্থ হ'ল প্রক্রিয়া বন্ধ করার জন্য আপনার নিজস্ব প্রক্রিয়াটি যুক্ত না করা (উদাহরণস্বরূপ CancellationToken) এটি নিজে থেকে ঘটবে না।
ওহাদ স্নাইডার

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

1
@ মিশেলফ্রিজেম আপনি var current = partition.Currentআগের মতো কিছু করতে পারেন await bodyএবং তারপরে currentধারাবাহিকতা ( ContinueWith(t => { ... }) ব্যবহার করতে পারেন ।
ওহাদ স্নাইডার

41

আপনি নতুন AsyncEnumerator নিউগেট প্যাকেজটির সাহায্যে প্রচেষ্টা বাঁচাতে পারেন , 4 বছর আগে যখন প্রশ্নটি মূলত পোস্ট করা হয়েছিল তখন এটি উপস্থিত ছিল না। এটি আপনাকে সমান্তরালতা ডিগ্রি নিয়ন্ত্রণ করতে দেয়:

using System.Collections.Async;
...

await ids.ParallelForEachAsync(async i =>
{
    ICustomerRepo repo = new CustomerRepo();
    var cust = await repo.GetCustomer(i);
    customers.Add(cust);
},
maxDegreeOfParallelism: 10);

দাবি অস্বীকার: আমি AsyncEnumerator লাইব্রেরির লেখক, যা ওপেন সোর্স এবং এমআইটির অধীনে লাইসেন্সযুক্ত, এবং আমি এই বার্তাটি পোস্ট করছি কেবল সম্প্রদায়কে সহায়তা করার জন্য।


11
সের্গেই, আপনাকে প্রকাশ করা উচিত যে আপনি গ্রন্থাগারের একজন লেখক
মাইকেল ফ্রেইজিম

5
ঠিক আছে, দাবি অস্বীকার। আমি এটির বিজ্ঞাপন দিয়ে কোনও সুবিধা চাইছি না, কেবল লোকদের সহায়তা করতে চাই;)
সার্জ সেমেনভ

আপনার লাইব্রেরি .NET কোর এর সাথে সামঞ্জস্যপূর্ণ নয়।
কর্নিয়েল নোবেল

2
@ কর্নিলনোবেল, এটি নেট কোরের সাথে সামঞ্জস্যপূর্ণ - গিটহাবের উত্স কোডটিতে নেট নেট ফ্রেমওয়ার্ক এবং। নেট কোর উভয়ের জন্য একটি পরীক্ষার কভারেজ রয়েছে।
সার্জ সেমেনভ

1
@ সার্জসেমেনভ আমি আপনার লাইব্রেরিটির জন্য প্রচুর ব্যবহার AsyncStreamsকরেছি এবং আমি এটি দুর্দান্ত বলেছি। এই লাইব্রেরি পর্যাপ্ত সুপারিশ করতে পারেন না।
ডাব্লুবুক

16

মোড়ানো Parallel.Foreachএকটি মধ্যে Task.Run()এবং পরিবর্তে awaitশব্দ ব্যবহার[yourasyncmethod].Result

(আপনার টাস্কটি করা দরকার U ইউআই থ্রেডটি ব্লক না করার জন্য চালনা করুন)

এটার মতো কিছু:

var yourForeachTask = Task.Run(() =>
        {
            Parallel.ForEach(ids, i =>
            {
                ICustomerRepo repo = new CustomerRepo();
                var cust = repo.GetCustomer(i).Result;
                customers.Add(cust);
            });
        });
await yourForeachTask;

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

@ লোনলিপিক্সেল আমার একমাত্র ইস্যুটি হ'ল Task.Runযখন TaskCompletionSourceপছন্দনীয় হয় তখন এটি কল করে ।
গুসডোর

1
@ গুডোর কৌতূহল - কেন বেশি পছন্দ TaskCompletionSource?
সীফিশ

@ সিফিশ একটি ভাল প্রশ্নের উত্তর আমি জানাতে চাই। অবশ্যই মোটামুটি এক দিন হতে হবে: ডি
গসডোর

একটি সংক্ষিপ্ত আপডেট। আমি এখন এটি ঠিক খুঁজছিলাম, সহজ সমাধানটি সন্ধান করতে স্ক্রোল করে আবার নিজের মন্তব্যটি পেয়েছি। আমি ঠিক এই কোডটি ব্যবহার করেছি এবং এটি প্রত্যাশার মতো কাজ করে। এটি কেবল ধরে নেয় যে লুপের মধ্যে আসল অ্যাসিঙ্ক কলগুলির একটি সিঙ্ক সংস্করণ রয়েছে। awaitঅতিরিক্ত ভেরিয়েবলের নাম সংরক্ষণ করতে সামনে সরিয়ে নেওয়া যায়।
ygoe

7

পুরো টিপিএল ডেটাফ্লো কাজ করার চেয়ে এটি বেশ দক্ষ এবং সহজ হওয়া উচিত:

var customers = await ids.SelectAsync(async i =>
{
    ICustomerRepo repo = new CustomerRepo();
    return await repo.GetCustomer(i);
});

...

public static async Task<IList<TResult>> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> selector, int maxDegreesOfParallelism = 4)
{
    var results = new List<TResult>();

    var activeTasks = new HashSet<Task<TResult>>();
    foreach (var item in source)
    {
        activeTasks.Add(selector(item));
        if (activeTasks.Count >= maxDegreesOfParallelism)
        {
            var completed = await Task.WhenAny(activeTasks);
            activeTasks.Remove(completed);
            results.Add(completed.Result);
        }
    }

    results.AddRange(await Task.WhenAll(activeTasks));
    return results;
}

ব্যবহারের উদাহরণ ব্যবহার করা উচিত নয় await: মত var customers = await ids.SelectAsync(async i => { ... });?
প্যাকসিসি

5

আমি পার্টিতে কিছুটা দেরি করেছি তবে আপনি আপনার অ্যাসিঙ্ক কোডটি সিঙ্কের প্রসঙ্গে চালাতে গেটআওয়েটার.গেটরসাল্ট () ব্যবহার করে বিবেচনা করতে চাইতে পারেন তবে নীচের মতো লম্বা;

 Parallel.ForEach(ids, i =>
{
    ICustomerRepo repo = new CustomerRepo();
    // Run this in thread which Parallel library occupied.
    var cust = repo.GetCustomer(i).GetAwaiter().GetResult();
    customers.Add(cust);
});

5

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

    /// <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);

5

একগুচ্ছ সহায়ক পদ্ধতি প্রবর্তনের পরে, আপনি এই সাধারণ বাক্য গঠন সহ সমান্তরাল ক্যোয়ারী চালাতে সক্ষম হবেন:

const int DegreeOfParallelism = 10;
IEnumerable<double> result = await Enumerable.Range(0, 1000000)
    .Split(DegreeOfParallelism)
    .SelectManyAsync(async i => await CalculateAsync(i).ConfigureAwait(false))
    .ConfigureAwait(false);

এখানে যা ঘটে তা হ'ল: আমরা উত্স সংগ্রহটি 10 ​​টি অংশ ( .Split(DegreeOfParallelism)) এ বিভক্ত করি , তারপরে প্রতিটি তার আইটেমগুলিকে একের পর এক প্রক্রিয়া করে (10 ) চালিত করি .SelectManyAsync(...)এবং সেগুলি একক তালিকায় আবার মার্জ করে।

এখানে উল্লেখযোগ্যভাবে একটি সহজ পদ্ধতির রয়েছে:

double[] result2 = await Enumerable.Range(0, 1000000)
    .Select(async i => await CalculateAsync(i).ConfigureAwait(false))
    .WhenAll()
    .ConfigureAwait(false);

তবে এটির একটি সতর্কতা দরকার : যদি আপনার উত্স সংগ্রহ খুব বড় হয় তবে এটি এখনই Taskপ্রতিটি আইটেমের জন্য নির্ধারিত হবে , যা পারফরম্যান্সে উল্লেখযোগ্য প্রভাব ফেলতে পারে।

উপরের উদাহরণগুলিতে ব্যবহৃত এক্সটেনশন পদ্ধতিগুলি নিম্নরূপ দেখুন:

public static class CollectionExtensions
{
    /// <summary>
    /// Splits collection into number of collections of nearly equal size.
    /// </summary>
    public static IEnumerable<List<T>> Split<T>(this IEnumerable<T> src, int slicesCount)
    {
        if (slicesCount <= 0) throw new ArgumentOutOfRangeException(nameof(slicesCount));

        List<T> source = src.ToList();
        var sourceIndex = 0;
        for (var targetIndex = 0; targetIndex < slicesCount; targetIndex++)
        {
            var list = new List<T>();
            int itemsLeft = source.Count - targetIndex;
            while (slicesCount * list.Count < itemsLeft)
            {
                list.Add(source[sourceIndex++]);
            }

            yield return list;
        }
    }

    /// <summary>
    /// Takes collection of collections, projects those in parallel and merges results.
    /// </summary>
    public static async Task<IEnumerable<TResult>> SelectManyAsync<T, TResult>(
        this IEnumerable<IEnumerable<T>> source,
        Func<T, Task<TResult>> func)
    {
        List<TResult>[] slices = await source
            .Select(async slice => await slice.SelectListAsync(func).ConfigureAwait(false))
            .WhenAll()
            .ConfigureAwait(false);
        return slices.SelectMany(s => s);
    }

    /// <summary>Runs selector and awaits results.</summary>
    public static async Task<List<TResult>> SelectListAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> selector)
    {
        List<TResult> result = new List<TResult>();
        foreach (TSource source1 in source)
        {
            TResult result1 = await selector(source1).ConfigureAwait(false);
            result.Add(result1);
        }
        return result;
    }

    /// <summary>Wraps tasks with Task.WhenAll.</summary>
    public static Task<TResult[]> WhenAll<TResult>(this IEnumerable<Task<TResult>> source)
    {
        return Task.WhenAll<TResult>(source);
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.