समवर्ती Iync / O संचालन की मात्रा को कैसे सीमित करें?


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 से अधिक वेब पेज डाउनलोड न हों। इसे सबसे कुशल तरीके से कैसे करें?


2
यह आपके पिछले प्रश्न से कैसे भिन्न है ?
२१

1
stackoverflow.com/questions/9290498/… एक समानांतर पैरामीटर के साथ।
क्रिस डिस्ले

4
@ क्रिसडिसले, यह केवल अनुरोधों के लॉन्च को समानांतर करेगा।
खर्चा

@vvick सही है, यह कैसे अलग है? btw, मुझे जवाब पसंद है stackoverflow.com/a/10802883/66372
eglasius

3
इसके अलावा HttpClientहै IDisposable, और आप इसे निपटाने चाहिए, खासकर जब आप उनमें से 1000 से अधिक उपयोग करने के लिए जा रहे हैं। HttpClientकई अनुरोधों के लिए एक सिंगलटन के रूप में इस्तेमाल किया जा सकता है।
शिम्मी वेइटहैंडलर

जवाबों:


161

आप .NET 4.5 बीटा का उपयोग करके .NET के लिए async के नवीनतम संस्करणों में निश्चित रूप से ऐसा कर सकते हैं। 'Usr' की पिछली पोस्ट स्टीफन टूब द्वारा लिखे गए एक अच्छे लेख की ओर इशारा करती है, लेकिन कम घोषित खबर यह है कि async semaphore ने वास्तव में इसे .NET 4.5 की बीटा रिलीज़ में बनाया था

यदि आप हमारे प्रिय SemaphoreSlimवर्ग को देखते हैं (जो आपको मूल से अधिक प्रदर्शन के बाद से उपयोग करना चाहिए Semaphore), तो अब यह दावा करता हैWaitAsync(...) ओवरलोड श्रृंखला का , सभी अपेक्षित तर्कों के साथ - टाइमआउट अंतराल, रद्द टोकन, आपके सभी सामान्य शेड्यूलिंग मित्र: )

स्टीफन ने नए .NET 4.5 के बारे में एक और हालिया ब्लॉग पोस्ट भी लिखा है जो बीटा के साथ सामने आया है। देखें कि क्या है। .NET के लिए नया क्या है।

अंतिम, यहाँ async विधि थ्रॉटलिंग के लिए SemaphoreSlim का उपयोग करने के बारे में कुछ नमूना कोड दिए गए हैं:

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)
}

अंतिम, लेकिन शायद एक योग्य उल्लेख एक समाधान है जो टीपीएल-आधारित शेड्यूलिंग का उपयोग करता है। आप TPL पर डेलिगेट-बाउंड कार्य बना सकते हैं जो अभी तक शुरू नहीं हुए हैं, और कॉन्सर्ट को सीमित करने के लिए एक कस्टम कार्य शेड्यूलर के लिए अनुमति देते हैं। वास्तव में, यहाँ इसके लिए एक MSDN नमूना है:

टास्कस्क्रिडर भी देखें ।


3
समानांतरवाद की एक सीमित डिग्री के साथ एक समानांतर नहीं है। एक अच्छा दृष्टिकोण? msdn.microsoft.com/en-us/library/…
GreyCloud

2
आप आपको क्यों नहीं HttpClient
मनाते

4
@GreyCloud: Parallel.ForEachसिंक्रोनस कोड के साथ काम करता है। इससे आप एसिंक्रोनस कोड को कॉल कर सकते हैं।
जोश नोह

2
@ गलत आप गलत कर रहे हैं । इसके अलावा यह हमेशा एक अच्छी आदत है कि सभी IDisposableएस usingया try-finallyबयानों को लपेटें , और उनके निपटान का आश्वासन दें।
शिमी वेइटहैंडलर

29
यह उत्तर कितना लोकप्रिय है, यह देखते हुए कि HttpClient अनुरोध के अनुसार उदाहरण के बजाय एक ही सामान्य उदाहरण हो सकता है और होना चाहिए।
रूपर्ट रॉन्स्ले

15

यदि आपके पास एक IEnumerable है (यानी URL के तार।) और आप इनमें से प्रत्येक के साथ एक I / O बाध्य ऑपरेशन करना चाहते हैं (यानी एक async http अनुरोध करें) समवर्ती और वैकल्पिक रूप से आप भी समवर्ती की अधिकतम संख्या सेट करना चाहते हैं। मैं / O वास्तविक समय में अनुरोध करता हूं, यहां बताया गया है कि आप ऐसा कैसे कर सकते हैं। इस तरह से आप थ्रेड पूल एट अल का उपयोग नहीं करते हैं, विधि अधिकतम समवर्ती I / O अनुरोधों को नियंत्रित करने के लिए अर्धचालक का उपयोग करता है जो एक स्लाइडिंग विंडो पैटर्न के समान होता है जिसे एक अनुरोध पूरा होता है, सेमीफोर छोड़ देता है और अगला एक अंदर हो जाता है।

उपयोग: प्रतीक्षा करें ForEachAsync (urlStrings, YourAsyncFunc, alternMaxDegreeOfConcurrency);

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
केवल सर्वोत्तम प्रथाओं और सबक के बारे में सोचकर हम अन्य लोगों को सिखाते हैं। A usingअच्छा होगा।
एजेंटफायर

अच्छी तरह से इस उदाहरण का मैं पालन कर सकता हूं, लेकिन यह काम करने की कोशिश करना कि ऐसा करने का सबसे अच्छा तरीका क्या है, मूल रूप से एक थ्रॉटलर है लेकिन मेरा फंक एक सूची लौटाएगा, जिसे मैं आखिरकार पूरा होने की अंतिम सूची में चाहता हूं ... जो हो सकता है सूची में लॉक करने की आवश्यकता है, क्या आपके पास सुझाव हैं।
सीबकिटक

आप विधि को थोड़ा अपडेट कर सकते हैं ताकि यह वास्तविक कार्यों की सूची लौटाए और आप अपने कॉलिंग कोड के अंदर से टास्क का इंतजार करें। टास्क.जब एक बार पूरा हो जाए, तो आप सूची में प्रत्येक कार्य पर गणना कर सकते हैं और इसकी सूची को अंतिम सूची में जोड़ सकते हैं। 'जनता स्थिर IEnumerable <टास्क <टाउट >> ForEachAsync <टिन, टाउट> (? IEnumerable <टिन> inputEnumerable, Func <टिन, टास्क <टाउट >> asyncProcessor, पूर्णांक maxDegreeOfParallelism = नल)' के लिए विधि हस्ताक्षर बदलें
Dogu अर्सलन

7

दुर्भाग्य से, .NET फ्रेमवर्क समानांतर async कार्यों को ऑर्केस्ट्रेट करने के लिए सबसे महत्वपूर्ण कॉम्बिनेटर गायब है। बिल्ट-इन जैसी कोई बात नहीं है।

सबसे सम्मानित स्टीफन Toub द्वारा निर्मित AsyncSemaphore वर्ग को देखें । आप जो चाहते हैं, उसे एक सेमाफोर कहा जाता है, और आपको इसके लिए एक async संस्करण की आवश्यकता होती है।


12
ध्यान दें कि "दुर्भाग्य से, .NET फ्रेमवर्क समानांतर async कार्यों को ऑर्केस्ट्रेट करने के लिए सबसे महत्वपूर्ण कॉम्बिनेटर गायब है। बिल्ट-इन जैसी कोई चीज नहीं है।" .NET 4.5 बीटा के अनुसार अब सही नहीं है। SemaphoreSlim अब WaitAsync (...) कार्यक्षमता प्रदान करता है :)
थियो यंग

क्या SemaphoreSlim (अपने नए async तरीकों के साथ) को AsyncSemphore पर पसंद किया जाना चाहिए, या Toub के कार्यान्वयन में अभी भी कुछ लाभ है?
टोड मेनियर

मेरी राय में, अंतर्निहित प्रकार को प्राथमिकता दी जानी चाहिए क्योंकि यह अच्छी तरह से परीक्षण और अच्छी तरह से डिजाइन किए जाने की संभावना है।
usr

4
स्टीफन ने अपने ब्लॉग पोस्ट पर एक सवाल के जवाब में एक टिप्पणी जोड़ते हुए पुष्टि की कि .NET 4.5 के लिए सेमाफोरस्लिम का उपयोग करना आमतौर पर जाने का तरीका होगा।
jdasilva 20

7

बहुत सारे नुकसान हैं और त्रुटि के मामलों में एक सीमाफोर का प्रत्यक्ष उपयोग मुश्किल हो सकता है, इसलिए मैं पहिया का फिर से आविष्कार करने के बजाय AsyncEnumerator NuGet पैकेज का उपयोग करने का सुझाव दूंगा :

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

यहां सेमाफोरस्लीम बहुत मददगार हो सकता है। यहाँ विस्तार विधि मैंने बनाई है।

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

पुराना सवाल, नया जवाब। @vitidev कोड का एक ब्लॉक था जिसका पुन: उपयोग एक परियोजना में लगभग बरकरार था जिसकी मैंने समीक्षा की। कुछ सहयोगियों के साथ चर्चा करने के बाद एक ने पूछा "आप केवल अंतर्निहित TPL विधियों का उपयोग क्यों नहीं करते हैं?" एक्शनब्लॉक वहां विजेता की तरह दिखता है। https://msdn.microsoft.com/en-us/library/hh194773(v=vs.110).aspx । संभवतः किसी भी मौजूदा कोड को बदलना समाप्त नहीं होगा, लेकिन निश्चित रूप से इस नगेट को अपनाने के लिए देखेंगे और थ्रॉटल्ड समानता के लिए मिस्टर सॉफ्टी के सर्वोत्तम अभ्यास का पुन: उपयोग करेंगे।


0

यहाँ एक समाधान है जो LINQ के आलसी स्वभाव का लाभ उठाता है। यह कार्यात्मक रूप से स्वीकृत उत्तर के बराबर है ), लेकिन इसके बजाय कार्यकर्ता-कार्यों का उपयोग करता है 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");
}

आउटपुट:

Url: https://stackoverflow.com , 105.574 chars
Url: https://superuser.com , 126.953 chars
Url: https://serverfault.com , 125.963 chars
Url: https://meta.stackexcode.com , 185.276 chars
...

माइक्रोसॉफ्ट के कार्यान्वयन की Task.WhenAllmaterializes तुरन्त एक सरणी के लिए गणनीय आपूर्ति की, एक ही बार में शुरू होता है करने के लिए सभी कार्य के कारण। हम ऐसा नहीं चाहते हैं, क्योंकि हम समवर्ती अतुल्यकालिक संचालन की संख्या को सीमित करना चाहते हैं। इसलिए हमें एक ऐसे विकल्प को लागू करने की आवश्यकता WhenAllहोगी, जो हमारे असंख्य को धीरे और धीरे-धीरे घेरेगा। हम यह करेंगे कि कई कार्यकर्ता-कार्य (संक्षिप्त स्तर के समतुल्य स्तर के बराबर) बनाकर, और प्रत्येक कार्यकर्ता-कार्य एक बार में हमारे गणना योग्य कार्य को करेगा, यह सुनिश्चित करने के लिए कि प्रत्येक url- कार्य संसाधित किया जाएगा। केवल एक कार्यकर्ता-कार्य द्वारा। फिर हम 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 + एक साथ डाउनलोड को आग लगा देंगे, भले ही यह एक ही थ्रेड पर चल रहा हो।
दुख कोडर

awaitवहां कीवर्ड नहीं देखा । इसे हटाकर समस्या को हल करना चाहिए, सही?
स्कूटम

2
लाइब्रेरी निश्चित रूप Runningसे कोर की मात्रा की तुलना में समवर्ती ( स्थिति के साथ ) चलने वाले अधिक कार्यों को संभाल सकती है । यह विशेष रूप से I / O बाध्य कार्य के साथ होगा।
21

@ स्विक: हां। क्या आप जानते हैं कि अधिकतम समवर्ती TPL कार्यों (धागे नहीं) को कुशलतापूर्वक कैसे नियंत्रित किया जाए?
शोक कोडर

-1

सीपीयू-बाउंड संचालन में तेजी के लिए समानांतर संगणना का उपयोग किया जाना चाहिए। यहां हम I / O बाध्य परिचालनों के बारे में बात कर रहे हैं। जब तक आप अपने मल्टी-कोर सीपीयू पर व्यस्त सिंगल कोर को भारी नहीं कर लेते, तब तक आपका कार्यान्वयन पूरी तरह से अनिवार्य होना चाहिए ।

EDIT मुझे यहाँ "async semaphore" का उपयोग करने के लिए usr द्वारा दिए गए सुझाव पसंद है।


अच्छी बात! हालांकि यहां प्रत्येक कार्य में async और सिंक कोड होगा (पृष्ठ असिंक्रोनस रूप से डाउनलोड किया गया है फिर सिंक तरीके से संसाधित किया गया है)। मैं CPU के पार कोड के सिंक भाग को वितरित करने का प्रयास कर रहा हूं और साथ ही समवर्ती async I / O संचालन की मात्रा को सीमित करता हूं।
दुख कोडर

क्यों? क्योंकि 1000+ http अनुरोधों को एक साथ लॉन्च करना उपयोगकर्ता की नेटवर्क क्षमता के अनुकूल कार्य नहीं हो सकता है।
खर्चा

समानांतर एक्सटेंशन भी मैं एक शुद्ध async समाधान को लागू करने के लिए बिना आई / ओ संचालन मल्टीप्लेक्स के लिए एक रास्ते के रूप में इस्तेमाल किया जा सकता है। जो मैं मानता हूं कि इसे मैला माना जा सकता है, लेकिन जब तक आप समवर्ती संचालन की संख्या पर एक तंग सीमा रखते हैं, यह संभवतः थ्रेडपूल को बहुत अधिक तनाव नहीं देगा।
सीन यू

3
मुझे नहीं लगता कि यह जवाब एक उत्तर प्रदान कर रहा है। विशुद्ध रूप से async होना यहाँ पर्याप्त नहीं है: हम वास्तव में गैर-अवरुद्ध तरीके से भौतिक IOs को थ्रॉटल करना चाहते हैं।
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
@NealEhardt सही है। समानांतर (जैसे विभिन्न थ्रेड्स पर) में सिंक्रोनस कोड के Parallel.ForEach(...)ब्लॉक चलाने के लिए है ।
थियो यंग

-1

अनिवार्य रूप से आप प्रत्येक URL के लिए एक क्रिया या कार्य बनाना चाहते हैं, जिसे आप हिट करना चाहते हैं, उन्हें एक सूची में डालें, और फिर उस सूची को संसाधित करें, जो उस संख्या को सीमित करता है जिसे समानांतर में संसाधित किया जा सकता है।

मेरा ब्लॉग पोस्ट पता चलता है कि टास्क के साथ और क्रियाओं के साथ यह कैसे करना है, और एक नमूना परियोजना प्रदान करता है जिसे आप डाउनलोड और चलाने के लिए दोनों को देख सकते हैं।

क्रियाओं के साथ

यदि क्रियाओं का उपयोग कर रहे हैं, तो आप बिल्ट-इन .Net समानांतर का उपयोग कर सकते हैं। इंवोक फ़ंक्शन। यहां हम इसे समानांतर में अधिकतम 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 पैरामीटर यानी मैक्सकाउंट को निर्दिष्ट करने की आवश्यकता है।
जय शाह

मैं सूची में संसाधित प्रत्येक कार्य से प्रत्येक प्रतिक्रिया चाहता हूं। मैं रिजल्ट या प्रतिक्रिया कैसे प्राप्त कर सकता हूं
venkat

-1

यह अच्छा अभ्यास नहीं है क्योंकि यह वैश्विक परिवर्तनशील है। यह भी async के लिए एक सामान्य समाधान नहीं है। लेकिन यह HttpClient के सभी उदाहरणों के लिए आसान है, अगर यह सब आपके बाद है। आप बस कोशिश कर सकते हैं:

System.Net.ServicePointManager.DefaultConnectionLimit = 20;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.