Ограничение количества задач

У меня есть код, запускающий процедуру загрузки данных для более чем 2000 компаний. Я изменил процедуру загрузки на 300-секундное ожидание, чтобы не усложнять этот пример. Ниже приведена процедура для одной компании, которой звонящий несколько раз вызывает.

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

От звонящего использую:

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) 

Что это делает, так это то, что он параллельно запускает все задачи, и задачи ставятся в очередь, пока не попадут в Интернет и не получат ответ. Из-за большого количества задач время ожидания многих задач истекает, потому что они не могут получить ответ за это время, поскольку количество задач, ожидающих такого ответа в любой момент времени, огромно. (пожалуйста, помните, что я удалил фактический код загрузки, чтобы все было просто, и заменил его длительной задачей, которая просто ждет 300 секунд в методе DoJob выше).

Я хочу ограничить количество запускаемых задач. Скажем, 50. Это будет означать, что в любой момент времени будут активны только 50 задач, остальные будут ждать завершения задач из группы из 50, а затем будут помещены в очередь по мере завершения задачи.

Я пробовал это:

Dim options As New ParallelOptions()
options.MaxDegreeOfParallelism = 100
Parallel.ForEach(companies, options, Sub(company)
                                                 ' logic
                                                 DoJob(company)
                                             End Sub)

Но похоже, что это запускает все задачи за один раз, а не сначала 100, а затем ждет (распечатки из DoJob приходят для всех 2000+ элементов, а затем задачи завершаются).

Такая же проблема и здесь:

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

Я пробовал пример @ClearLogics в Как чтобы ограничить максимальное количество параллельных задач в C #

Он также показывает такое же поведение. Все задачи сразу запускаются.

Как мне обойти это - просто нужно запустить 100 задач и подождать, а затем продолжать стоять в очереди, чтобы в любой момент у меня не было более 100 задач.


person Kallol    schedule 27.08.2017    source источник


Ответы (1)


Ваше ожидание относительно MaxDegreeOfParallelism неверно, см. эту статью , что объясняет, почему вы можете видеть, что одновременно запущено 2000 потоков. Когда Await Task.Delay(1000).ConfigureAwait(False) запускается, поток считается свободным и может начать выполнение следующей задачи.

Я хочу ограничить количество запускаемых задач.

Вы должны реализовать это самостоятельно. Вы можете использовать два подхода:

  1. Внедрите свой собственный TaskScheduller, как в эту статью.
  2. Управляйте количеством созданных задач на более высоком уровне, например, используя семафор для управления параллельными задачами. Пример можно найти здесь.

А если вы выберете второй вариант, предупреждаю, что это всего лишь иллюстрация работы с Семафором. Я имею в виду, что код не для продакшена.

person cassandrad    schedule 28.08.2017