সাম্প্রতিক async আই / ও অপারেশনগুলির পরিমাণ কীভাবে সীমাবদ্ধ করবেন?


115
// let's say there is a list of 1000+ URLs
string[] urls = { "http://google.com", "http://yahoo.com", ... };

// now let's send HTTP requests to each of these URLs in parallel
urls.AsParallel().ForAll(async (url) => {
    var client = new HttpClient();
    var html = await client.GetStringAsync(url);
});

সমস্যাটি এখানে, এটি 1000+ একযোগে ওয়েব অনুরোধ শুরু করে। এই অ্যাসিঙ্ক http অনুরোধগুলির একযোগে পরিমাণ সীমাবদ্ধ করার কি সহজ উপায় আছে? যাতে কোনও নির্দিষ্ট সময়ে 20 টিরও বেশি ওয়েব পৃষ্ঠাগুলি ডাউনলোড হয় না। সবচেয়ে দক্ষ পদ্ধতিতে এটি কীভাবে করবেন?



1
stackoverflow.com/questions/9290498/… সমান্তরাল অপশন প্যারামিটার সহ।
ক্রিস ডিসলে

4
@ ক্রিসডিসলে, এটি কেবল অনুরোধগুলির প্রবর্তনকে সামঞ্জস্য করবে।
ব্যয়কারী

@ স্পিক ঠিক বলেছেন, এটি কীভাবে আলাদা? BTW, আমি উত্তর ভালবাসেন সেখানে stackoverflow.com/a/10802883/66372
eglasius

3
এছাড়া HttpClientহয় IDisposableএবং আপনি এটি নিষ্পত্তি করা উচিত, বিশেষ করে যখন আপনি তাদের 1000+ ব্যবহার করতে যাচ্ছি। HttpClientএকাধিক অনুরোধের জন্য একক হিসাবে ব্যবহার করা যেতে পারে।
শিমি ওয়েটজ্যান্ডলার

উত্তর:


161

আপনি নেট .NET 4.5 বিটা ব্যবহার করে নেট .NET- র জন্য সর্বশেষ সংস্করণগুলিতে অবশ্যই এটি করতে পারেন। 'ইউএসআর' এর আগের পোস্টটি স্টিফেন টুব দ্বারা রচিত একটি ভাল নিবন্ধের দিকে ইঙ্গিত করেছে, তবে কম ঘোষিত সংবাদটি হ'ল অ্যাসিঙ্ক সেম্যাফোরটি প্রকৃতপক্ষে .NET 4.5 এর বিটা প্রকাশে পরিণত করেছে

যদি আপনি আমাদের প্রিয় SemaphoreSlimশ্রেণীর দিকে নজর দেন (যা আপনি এটির তুলনায় মূল ব্যবহারের চেয়ে বেশি ব্যবহার করা উচিত Semaphore) তবে এটি এখন WaitAsync(...)আপনার প্রত্যাশিত সমস্ত যুক্তি - সময়সীমার অন্তর, বাতিলকরণ টোকেন, আপনার সমস্ত সাধারণ সময়সূচী বন্ধুদের সাথে: ওভারলোডের সিরিজটি নিয়ে গর্ব করে : )

স্টিফেন নতুন নতুন নেট সম্পর্কে আরও একটি সাম্প্রতিক ব্লগ পোস্ট লিখেছেন 4.5.৫ নেট গুডি যা বিটা নিয়ে এসেছিল তা । নেট 4.5.৪ বিটাতে সমান্তরালতার জন্য নতুন কী দেখুন ।

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

public async Task MyOuterMethod()
{
    // let's say there is a list of 1000+ URLs
    var urls = { "http://google.com", "http://yahoo.com", ... };

    // now let's send HTTP requests to each of these URLs in parallel
    var allTasks = new List<Task>();
    var throttler = new SemaphoreSlim(initialCount: 20);
    foreach (var url in urls)
    {
        // do an async wait until we can schedule again
        await throttler.WaitAsync();

        // using Task.Run(...) to run the lambda in its own parallel
        // flow on the threadpool
        allTasks.Add(
            Task.Run(async () =>
            {
                try
                {
                    var client = new HttpClient();
                    var html = await client.GetStringAsync(url);
                }
                finally
                {
                    throttler.Release();
                }
            }));
    }

    // won't get here until all urls have been put into tasks
    await Task.WhenAll(allTasks);

    // won't get here until all tasks have completed in some way
    // (either success or exception)
}

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

টাস্কশেডুলারও দেখুন ।


3
সমান্তরালতার সীমিত ডিগ্রির সাথে একটি সমান্তরাল.ফৌচ নয় যা ভাল পদ্ধতির? msdn.microsoft.com/en-us/library/…
গ্রেই ক্লাউড

2
আপনি কেন নিষ্পত্তি করবেন নাHttpClient
শিমি ওয়েটস্যান্ডলার

4
@ গ্রেই ক্লাউড: Parallel.ForEachসিঙ্ক্রোনাস কোড নিয়ে কাজ করে। এটি আপনাকে অ্যাসিক্রোনাস কোড কল করতে দেয়।
জোশ নো

2
@TheMonarch তুমি ভুল । এছাড়া এটা সবসময় সব মোড়ানো একটি ভাল অভ্যাস IDisposableমধ্যে গুলি usingবা try-finallyবিবৃতি, এবং তাদের নিষ্পত্তি করেছেন।
শিমি ওয়েটজ্যান্ডলার 4

29
এই উত্তরটি কতটা জনপ্রিয় তা প্রদত্ত, এটি উল্লেখ করা মূল্যবান যে HTTPClient প্রতি অনুরোধের পরিবর্তে একক সাধারণ উদাহরণ হতে পারে এবং হওয়া উচিত।
রূপার্ট রাউন্সলে

15

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

ব্যবহার: অপেক্ষারত ফরইচএ্যানসিঙ্ক (urlStrings, YourAsyncFunc, alচ্ছিকম্যাক্সডিজারিওফকনক্রেন্সি);

public static Task ForEachAsync<TIn>(
        IEnumerable<TIn> inputEnumerable,
        Func<TIn, Task> asyncProcessor,
        int? maxDegreeOfParallelism = null)
    {
        int maxAsyncThreadCount = maxDegreeOfParallelism ?? DefaultMaxDegreeOfParallelism;
        SemaphoreSlim throttler = new SemaphoreSlim(maxAsyncThreadCount, maxAsyncThreadCount);

        IEnumerable<Task> tasks = inputEnumerable.Select(async input =>
        {
            await throttler.WaitAsync().ConfigureAwait(false);
            try
            {
                await asyncProcessor(input).ConfigureAwait(false);
            }
            finally
            {
                throttler.Release();
            }
        });

        return Task.WhenAll(tasks);
    }


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

1
আমরা অন্যান্য লোকদের শেখানোর সেরা অনুশীলন এবং পাঠের কথা চিন্তা করি। একটি usingসুন্দর হবে।
এজেন্টফায়ার

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

আপনি সামান্য পদ্ধতিটি আপডেট করতে পারেন যাতে এটি প্রকৃত কার্যগুলির তালিকায় ফিরে আসে এবং আপনি আপনার কলিং কোডের ভিতরে থেকে কার্যটির জন্য অপেক্ষা করেন hen একবার Task.WhenAll সম্পূর্ণ হয়ে গেলে আপনি তালিকার প্রতিটি কাজ গণনা করতে পারেন এবং এর তালিকাটি চূড়ান্ত তালিকায় যুক্ত করতে পারেন। পাবলিক স্ট্যাটিক IEnumerable <টাস্ক <দালালি >> ForEachAsync <টিন, দালালি> (? IEnumerable <টিনের> inputEnumerable, Func <টিন, টাস্ক <দালালি >> asyncProcessor, int- maxDegreeOfParallelism = নাল) 'থেকে পদ্ধতি স্বাক্ষর পরিবর্তন
Dogu আরসালান

7

দুর্ভাগ্যক্রমে, .NET ফ্রেমওয়ার্ক সমান্তরাল অ্যাসিঙ্ক কার্যগুলিকে অর্কেস্ট্রেট করার জন্য সবচেয়ে গুরুত্বপূর্ণ সংযুক্তকারীগুলি অনুপস্থিত। অন্তর্নির্মিত তেমন কোনও জিনিস নেই।

সর্বাধিক সম্মানজনক স্টিফেন টুব দ্বারা নির্মিত অ্যাসিঙ্কসেমাপোর ক্লাসটি দেখুন । আপনি যা চান সেমফোর বলা হয় এবং এটির একটি অ্যাসিঙ্ক সংস্করণ আপনার প্রয়োজন।


12
মনে রাখবেন যে "দুর্ভাগ্যক্রমে, .NET ফ্রেমওয়ার্ক সমান্তরাল অ্যাসিঙ্ক কার্যগুলিকে অর্কেস্ট্রেট করার জন্য সবচেয়ে গুরুত্বপূর্ণ সংযুক্তকারীগুলি অনুপস্থিত। এখানে অন্তর্নির্মিত কোনও জিনিস নেই" " .NET 4.5 বিটা হিসাবে আর সঠিক নয়। সেম্যাফোরস্লিম এখন ওয়েটএন্সিঙ্ক (...) কার্যকারিতা সরবরাহ করে :)
থিও ইয়ুং 30'12

এসিঙ্কসেম্পোরের চেয়ে সেম্যাফোরস্লিম (তার নতুন অ্যাসিঙ্ক পদ্ধতিগুলি সহ) পছন্দ করা উচিত, বা তৌব এর বাস্তবায়নের এখনও কিছু সুবিধা রয়েছে?
টড মেনিয়ার

আমার মতে, অন্তর্নির্মিত ধরণের পছন্দ করা উচিত কারণ এটি সম্ভবত ভাল-পরীক্ষিত এবং ভালভাবে ডিজাইন করা হতে পারে।
usr

4
স্টিফেন তার ব্লগ পোস্টে একটি প্রশ্নের জবাবে একটি মন্তব্য যুক্ত করেছেন তা নিশ্চিত করে যে .NET 4.5 এর জন্য সেমফোরস্লিম ব্যবহার করা সাধারণত উপায় হবে।
jdasilva

7

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

// let's say there is a list of 1000+ URLs
string[] urls = { "http://google.com", "http://yahoo.com", ... };

// now let's send HTTP requests to each of these URLs in parallel
await urls.ParallelForEachAsync(async (url) => {
    var client = new HttpClient();
    var html = await client.GetStringAsync(url);
}, maxDegreeOfParalellism: 20);

4

থিও ইয়ুং উদাহরণটি দুর্দান্ত, তবে অপেক্ষার কার্যের তালিকা ছাড়াই একটি বৈকল্পিক রয়েছে।

 class SomeChecker
 {
    private const int ThreadCount=20;
    private CountdownEvent _countdownEvent;
    private SemaphoreSlim _throttler;

    public Task Check(IList<string> urls)
    {
        _countdownEvent = new CountdownEvent(urls.Count);
        _throttler = new SemaphoreSlim(ThreadCount); 

        return Task.Run( // prevent UI thread lock
            async  () =>{
                foreach (var url in urls)
                {
                    // do an async wait until we can schedule again
                    await _throttler.WaitAsync();
                    ProccessUrl(url); // NOT await
                }
                //instead of await Task.WhenAll(allTasks);
                _countdownEvent.Wait();
            });
    }

    private async Task ProccessUrl(string url)
    {
        try
        {
            var page = await new WebClient()
                       .DownloadStringTaskAsync(new Uri(url)); 
            ProccessResult(page);
        }
        finally
        {
            _throttler.Release();
            _countdownEvent.Signal();
        }
    }

    private void ProccessResult(string page){/*....*/}
}

4
দ্রষ্টব্য, এই পদ্ধতির ব্যবহারের একটি বিপদ রয়েছে - যে ব্যতিক্রমগুলি ঘটে থাকে ProccessUrlবা এর সাব-ফাংশনগুলি আসলে উপেক্ষা করা হবে। এগুলি টাস্কগুলিতে বন্দী হবে, তবে আসল কলারে ফেরত দেওয়া হয়নি Check(...)। ব্যক্তিগতভাবে, এ কারণেই আমি এখনও ত্রুটি প্রচারের জন্য টাস্ক এবং তাদের সংযুক্তকারী ফাংশনগুলি WhenAllএবং এর মতো ব্যবহার করি WhenAny। :)
থিও ইয়ুং

3

SemaphoreSlim এখানে খুব সহায়ক হতে পারে। আমি তৈরি করেছি এমন এক্সটেনশন পদ্ধতিটি এখানে।

    /// <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="maxActionsToRunInParallel">Optional, max numbers of the actions to run in parallel,
    /// 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? maxActionsToRunInParallel = null)
    {
        if (maxActionsToRunInParallel.HasValue)
        {
            using (var semaphoreSlim = new SemaphoreSlim(
                maxActionsToRunInParallel.Value, maxActionsToRunInParallel.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 of the provided 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);

0

পুরানো প্রশ্ন, নতুন উত্তর। @ভিটিদেবের কোডগুলির একটি ব্লক ছিল যা আমি পর্যালোচনা করা একটি প্রকল্পে প্রায় অক্ষত পুনরায় ব্যবহার করা হয়েছিল। কয়েকজন সহকর্মীর সাথে আলোচনার পরে একজন জিজ্ঞাসা করলেন "আপনি কেবল বিল্ট-ইন টিপিএল পদ্ধতি ব্যবহার করেন না কেন?" অ্যাকশনব্লক সেখানে বিজয়ীর মতো দেখাচ্ছে। https://msdn.microsoft.com/en-us/library/hh194773(v=vs.110).aspx । সম্ভবত কোনও বিদ্যমান কোড পরিবর্তন করা শেষ হবে না তবে অবশ্যই এই নোটটি গ্রহণ করবে এবং থ্রোটলেড সমান্তরালতার জন্য মিঃ সফ্টির সেরা অনুশীলনটি পুনরায় ব্যবহার করবে।


0

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

string[] urls =
{
    "https://stackoverflow.com",
    "https://superuser.com",
    "https://serverfault.com",
    "https://meta.stackexchange.com",
    // ...
};
var httpClient = new HttpClient();
var tasks = urls.Select(async (url) =>
{
    return (Url: url, Html: await httpClient.GetStringAsync(url));
});

দ্বিতীয় পদক্ষেপটি awaitএকযোগে Task.WhenAllপদ্ধতিটি ব্যবহার করে সমস্ত কাজ করা :

var results = await Task.WhenAll(tasks);
foreach (var result in results)
{
    Console.WriteLine($"Url: {result.Url}, {result.Html.Length:#,0} chars");
}

আউটপুট:

ইউআরএল: https://stackoverflow.com , 105.574 অক্ষর
url: https://superuser.com , 126.953 চরগুলি
url: https://serverfault.com , 125.963 চরগুলি
url: https://meta.stackexchange.com , 185.276 অক্ষর
...

মাইক্রোসফ্ট বাস্তবায়িত হয়ে মাইক্রোসফ্টেরTask.WhenAll তাত্ক্ষণিকভাবে সরবরাহ করা হবে এমন একটি অ্যারের পরিগণিত, যার ফলে সমস্ত কাজ একসাথে শুরু হয়ে যায়। আমরা এটি চাই না, কারণ আমরা সমকালীন অ্যাসিনক্রোনাস অপারেশনগুলির সংখ্যা সীমাবদ্ধ করতে চাই। সুতরাং আমাদের WhenAllএমন একটি বিকল্প বাস্তবায়ন করতে হবে যা আমাদের গণ্যমানকে আস্তে আস্তে এবং ধীরে ধীরে গণ্য করবে। আমরা বহু কর্মী-কার্য তৈরি করে (সমঝোতার পছন্দসই স্তরের সমান) তৈরি করব এবং প্রতিটি কর্মী-কার্য একটি সময়ে আমাদের সংখ্যাগরিষ্ঠ একটি কাজ গণনা করবে এবং প্রতিটি ইউআরএল-টাস্ক প্রক্রিয়াজাত হবে তা নিশ্চিত করার জন্য একটি লক ব্যবহার করে will শুধুমাত্র একটি কর্মী-কাজ দ্বারা। তারপরে আমরা awaitসমস্ত কর্মী-কাজ সমাপ্ত করার জন্য এবং অবশেষে আমরা ফলাফলগুলি ফিরিয়ে দিই। বাস্তবায়ন এখানে:

public static async Task<T[]> WhenAll<T>(IEnumerable<Task<T>> tasks,
    int concurrencyLevel)
{
    if (tasks is ICollection<Task<T>>) throw new ArgumentException(
        "The enumerable should not be materialized.", nameof(tasks));
    var locker = new object();
    var results = new List<T>();
    var failed = false;
    using (var enumerator = tasks.GetEnumerator())
    {
        var workerTasks = Enumerable.Range(0, concurrencyLevel)
        .Select(async _ =>
        {
            try
            {
                while (true)
                {
                    Task<T> task;
                    int index;
                    lock (locker)
                    {
                        if (failed) break;
                        if (!enumerator.MoveNext()) break;
                        task = enumerator.Current;
                        index = results.Count;
                        results.Add(default); // Reserve space in the list
                    }
                    var result = await task.ConfigureAwait(false);
                    lock (locker) results[index] = result;
                }
            }
            catch (Exception)
            {
                lock (locker) failed = true;
                throw;
            }
        }).ToArray();
        await Task.WhenAll(workerTasks).ConfigureAwait(false);
    }
    lock (locker) return results.ToArray();
}

... এবং এখানে আমাদের প্রয়োজনীয় কোডটি পরিবর্তন করতে হবে, কাঙ্ক্ষিত থ্রোটলিং অর্জন করতে:

var results = await WhenAll(tasks, concurrencyLevel: 2);

ব্যতিক্রমগুলি পরিচালনা করার ক্ষেত্রে একটি পার্থক্য রয়েছে। নেটিভ Task.WhenAllসমস্ত কাজ শেষ হওয়ার জন্য অপেক্ষা করে এবং সমস্ত ব্যতিক্রমকে একত্রিত করে। উপরের বাস্তবায়নটি প্রথম ত্রুটিযুক্ত কাজ শেষ হওয়ার পরে অবিলম্বে বন্ধ করে দেয়।


এসি # 8 বাস্তবায়ন আয় একটি যে IAsyncEnumerable<T>খুঁজে পাওয়া যেতে পারে এখানে
থিওডর জৌলিয়াস

-1

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


8
হ্যাঁ, তবে এটি async I / O ক্রিয়াকলাপের সাথে সম্পর্কিত নয়। উপরের কোডটি একক থ্রেডে চললেও 1000+ একযোগে ডাউনলোডগুলি আপ করবে fire
দুঃখ কোডার

awaitসেখানে কীওয়ার্ডটি দেখেনি । এটি মুছে ফেলা সমস্যার সমাধান করা উচিত, সঠিক?
স্কটম

2
গ্রন্থাগারটি অবশ্যই Runningকরের পরিমাণের চেয়ে একযোগে চলমান আরও বেশি কার্য ( স্থিতি সহ) পরিচালনা করতে পারে । এটি আই / ও বাউন্ড টাস্কগুলির ক্ষেত্রে বিশেষত হবে।
সোভিক

@ এসভিক: হ্যাঁ আপনি কীভাবে সর্বাধিক সমবর্তী টিপিএল কার্যগুলি দক্ষতার সাথে নিয়ন্ত্রণ করতে চান (থ্রেডগুলি নয়)?
দুঃখ কোডার

-1

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

সম্পাদনা করুন আমার এখানে একটি "অ্যাসিঙ্ক সেম্যাফোর" ব্যবহার করার জন্য ইউএসআর দ্বারা প্রস্তাবিত পরামর্শটি পছন্দ করি।


ভাল যুক্তি! যদিও এখানে প্রতিটি কার্যক্রমে অ্যাসিঙ্ক এবং সিঙ্ক কোড থাকবে (পৃষ্ঠাটি ততক্ষণে সিঙ্ক পদ্ধতিতে ডাউনলোড করা হবে)। আমি সিপিইউজ জুড়ে কোডের সিঙ্ক অংশটি বিতরণ করার চেষ্টা করছি এবং একই সাথে একত্রে অসম্পূর্ণ আই / ও ক্রিয়াকলাপের পরিমাণ সীমাবদ্ধ করব।
দুঃখ কোডার

কেন? কারণ একসাথে 1000+ টি অনুরোধ প্রবর্তন করা কোনও ব্যবহারকারীর নেটওয়ার্ক ক্ষমতার জন্য উপযুক্ত নয়।
ব্যয়কারী

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

3
আমি মনে করি না যে এই উত্তরটি কোনও উত্তর সরবরাহ করছে। বিশুদ্ধরূপে অ্যাসিঙ্ক হওয়া এখানে পর্যাপ্ত নয়: আমরা প্রকৃতপক্ষে শারীরিক আইওগুলিকে একটি অবরুদ্ধকরণ পদ্ধতিতে থ্রটল করতে চাই।
usr

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

-1

MaxDegreeOfParallelismআপনি যা নির্দিষ্ট করতে পারেন সেই বিকল্পটি ব্যবহার করুন Parallel.ForEach():

var options = new ParallelOptions { MaxDegreeOfParallelism = 20 };

Parallel.ForEach(urls, options,
    url =>
        {
            var client = new HttpClient();
            var html = client.GetStringAsync(url);
            // do stuff with html
        });

4
আমি মনে করি না এটি কাজ করে। GetStringAsync(url)সাথে ডাকা বোঝানো হয় await। আপনি যদি প্রকারটি পরীক্ষা করেন var htmlতবে এটি একটি Task<string>, ফলাফল নয় string
নিল এয়ার্ড্ট

2
পছন্দ করুন সমান্তরাল (যেমন বিভিন্ন থ্রেড) সিঙ্ক্রোনাস কোড Parallel.ForEach(...)ব্লক চলমান জন্য উদ্দিষ্ট ।
থিও ইয়াং

-1

মূলত আপনি যে URL টি হিট করতে চান তার জন্য একটি ক্রিয়া বা টাস্ক তৈরি করতে চান, তাদের একটি তালিকায় রাখুন এবং তারপরে সেই তালিকাটি প্রক্রিয়া করুন, যে সংখ্যাটি সমান্তরালভাবে প্রক্রিয়াজাত হতে পারে সীমাবদ্ধ করে।

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

ক্রিয়া সহ

অ্যাকশনগুলি ব্যবহার করে, আপনি অন্তর্নির্মিত। নেট সমান্তরাল ব্যবহার করতে পারেন n এখানে আমরা এটিকে সমান্তরালভাবে প্রায় 20 টি থ্রেডে চালিত করতে সীমাবদ্ধ করি।

var listOfActions = new List<Action>();
foreach (var url in urls)
{
    var localUrl = url;
    // Note that we create the Task here, but do not start it.
    listOfTasks.Add(new Task(() => CallUrl(localUrl)));
}

var options = new ParallelOptions {MaxDegreeOfParallelism = 20};
Parallel.Invoke(options, listOfActions.ToArray());

টাস্ক সহ

টাস্ক সহ কোনও বিল্ট-ইন ফাংশন নেই। তবে, আমি আমার ব্লগে যেটি সরবরাহ করি তা আপনি ব্যবহার করতে পারেন।

    /// <summary>
    /// Starts the given tasks and waits for them to complete. This will run, at most, the specified number of tasks in parallel.
    /// <para>NOTE: If one of the given tasks has already been started, an exception will be thrown.</para>
    /// </summary>
    /// <param name="tasksToRun">The tasks to run.</param>
    /// <param name="maxTasksToRunInParallel">The maximum number of tasks to run in parallel.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    public static async Task StartAndWaitAllThrottledAsync(IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = new CancellationToken())
    {
        await StartAndWaitAllThrottledAsync(tasksToRun, maxTasksToRunInParallel, -1, cancellationToken);
    }

    /// <summary>
    /// Starts the given tasks and waits for them to complete. This will run the specified number of tasks in parallel.
    /// <para>NOTE: If a timeout is reached before the Task completes, another Task may be started, potentially running more than the specified maximum allowed.</para>
    /// <para>NOTE: If one of the given tasks has already been started, an exception will be thrown.</para>
    /// </summary>
    /// <param name="tasksToRun">The tasks to run.</param>
    /// <param name="maxTasksToRunInParallel">The maximum number of tasks to run in parallel.</param>
    /// <param name="timeoutInMilliseconds">The maximum milliseconds we should allow the max tasks to run in parallel before allowing another task to start. Specify -1 to wait indefinitely.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    public static async Task StartAndWaitAllThrottledAsync(IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, int timeoutInMilliseconds, CancellationToken cancellationToken = new CancellationToken())
    {
        // Convert to a list of tasks so that we don't enumerate over it multiple times needlessly.
        var tasks = tasksToRun.ToList();

        using (var throttler = new SemaphoreSlim(maxTasksToRunInParallel))
        {
            var postTaskTasks = new List<Task>();

            // Have each task notify the throttler when it completes so that it decrements the number of tasks currently running.
            tasks.ForEach(t => postTaskTasks.Add(t.ContinueWith(tsk => throttler.Release())));

            // Start running each task.
            foreach (var task in tasks)
            {
                // Increment the number of tasks currently running and wait if too many are running.
                await throttler.WaitAsync(timeoutInMilliseconds, cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();
                task.Start();
            }

            // Wait for all of the provided tasks to complete.
            // We wait on the list of "post" tasks instead of the original tasks, otherwise there is a potential race condition where the throttler's using block is exited before some Tasks have had their "post" action completed, which references the throttler, resulting in an exception due to accessing a disposed object.
            await Task.WhenAll(postTaskTasks.ToArray());
        }
    }

এবং তারপরে আপনার কার্যগুলির তালিকা তৈরি করুন এবং ফাংশনটি চালানোর জন্য কল করুন, একবারে সর্বোচ্চ 20 একসাথে বলে, আপনি এটি করতে পারেন:

var listOfTasks = new List<Task>();
foreach (var url in urls)
{
    var localUrl = url;
    // Note that we create the Task here, but do not start it.
    listOfTasks.Add(new Task(async () => await CallUrl(localUrl)));
}
await Tasks.StartAndWaitAllThrottledAsync(listOfTasks, 20);

আমি মনে করি আপনি কেবল সেমফোরস্লিমের জন্য প্রাথমিক হিসাব নির্দিষ্ট করে দিচ্ছেন এবং আপনাকে সেমাফোরস্লিমের কনস্ট্রাক্টরে 2 য় প্যারামিটার অর্থ ম্যাক্সাউন্ট নির্দিষ্ট করতে হবে।
জে শাহ

আমি প্রতিটি কাজ থেকে প্রতিটি প্রতিক্রিয়া একটি তালিকাতে প্রক্রিয়াজাত করতে চাই। আমি কিভাবে রিটার্ন ফল বা প্রতিক্রিয়া পেতে পারেন
ভেঙ্কট

-1

এটি বিশ্বব্যাপী পরিবর্তনশীল পরিবর্তনের কারণে এটি ভাল অনুশীলন নয়। এটি async এর সাধারণ সমাধানও নয়। তবে আপনি যদি পরে থাকেন তবে এইচটিটিপিপ্লিনেন্টের সমস্ত দৃষ্টান্তের পক্ষে এটি সহজ। আপনি কেবল চেষ্টা করতে পারেন:

System.Net.ServicePointManager.DefaultConnectionLimit = 20;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.