I have a code that fires download data routine for 2000+ companies. I have changed the download routine as a 300 seconds wait for the sake of keeping this example simple. The below is the routine for a single company which is called multiple times by the caller.
Public Async Function DoJob(ByVal company As Company) As Task(Of Boolean)
Console.WriteLine(String.Format("Started:{0}", company.CompanySymbol))
For i As Long = 1 To 300
Await Task.Delay(1000).ConfigureAwait(False)
Next
Console.WriteLine(String.Format("Ended:{0}", company.CompanySymbol))
Return True
End Function
From the caller, I use:
Dim downloadTasksQuery As IEnumerable(Of Task(Of Boolean)) =
From company In companies Select DoJob(company)
'***Use ToList to execute the query And start the download tasks.
Dim downloadTasks As IEnumerable(Of Task(Of Boolean)) = downloadTasksQuery.ToList()
Await Task.WhenAll(downloadTasks)
What this does is it in parallel fires all the tasks and the tasks get queued till it gets to the internet and has a response back. Due to the number of the tasks being high, lot of tasks are getting timed out because they are unable to get a response back within the time as the number of tasks waiting for such response at any point of time is huge. (pls remember I have removed the actual code of download to keep things simple and substituted by a long running task that just waits for 300 seconds in the DoJob method above).
What I want to do is limit the number of tasks that can be fired. Say, 50. That would mean at any point of time only 50 tasks will be active, rest would wait for tasks to complete from the bunch of 50 and then get queued as and when a task completes.
I have tried this:
Dim options As New ParallelOptions()
options.MaxDegreeOfParallelism = 100
Parallel.ForEach(companies, options, Sub(company)
' logic
DoJob(company)
End Sub)
But it looks like this is firing all the tasks in one go, instead of 100 first and then waiting ( The prints from DoJob
come for all 2000+ items and then the tasks gets completed).
Same problem here too:
Dim listOfActions = New List(Of Action)()
For Each company In companies
' Note that we create the Action here, but do not start it.
listOfActions.Add(Function() DoJob(company))
Next
Dim options As New ParallelOptions()
options.MaxDegreeOfParallelism = 100
Parallel.Invoke(options, listOfActions.ToArray())
I have tried @ClearLogics example in How to limit the Maximum number of parallel tasks in c#
It is also showing the same behavior. All tasks get fired at once.
How do I get around this - just need to fire 100 tasks and wait subsequently keep queuing so that at any point, I dont have more than 100 tasks.
Your expectation about MaxDegreeOfParallelism
is incorrect, please look at this article, which explains why you can see 2000 threads started at the same time. When Await Task.Delay(1000).ConfigureAwait(False)
gets executed, thread is assumed to be free and can start on next task.
What I want to do is limit the number of tasks that can be fired.
You have to implement it by yourself. You can take two approaches:
TaskScheduller
like in this article.And in case if you will pick the second option, I warn you that it's just an illustration of work with Semaphore. I mean, the code is not for production.