Parallel.For зависает после 1370 итераций, не знаю почему - PullRequest
10 голосов
/ 03 декабря 2010

Я запускаю цикл Parallel.For для немногим более 7500 объектов.Внутри цикла for я делаю несколько вещей для каждого из этих объектов, в частности, вызываю два веб-сервиса и два внутренних метода.Веб-сервисы просто проверяют объект, обрабатывают и возвращают строку, которую я затем устанавливаю как свойство объекта.То же самое касается двух внутренних методов.

Я ничего не записываю на диск или не читаю с диска.

Я также обновляю пользовательский интерфейс в приложении winforms с меткой и индикатором выполнения, чтобысообщите пользователю, где он находится.Вот код:

var task = Task.Factory.StartNew(() =>
{
  Parallel.For(0, upperLimit, (i, loopState) =>
  {
     if (cancellationToken.IsCancellationRequested)
        loopState.Stop();
     lblProgressBar.Invoke(
       (Action)
       (() => lblProgressBar.Text = string.Format("Processing record {0} of {1}.", (progressCounter++), upperLimit)));
     progByStep.Invoke(
       (Action)
       (() => progByStep.Value = (progressCounter - 1)));

      CallSvc1(entity[i]);
      Conversion1(entity[i]);
      CallSvc2(entity[i]);
      Conversion2(entity[i]);
  });
}, cancellationToken);

Это происходит на 32-битной машине Win7.

Любые идеи относительно того, почему это внезапно замирает, когда инкрементатор составляет около 1370 или около того (это было 1361,1365 и 1371)?

Любые идеи относительно того, как я могу отладить это и посмотреть, что блокирует, если что-нибудь?

РЕДАКТИРОВАТЬ:
Некоторые ответы на комментарии ниже:
@BrokenGlass - Нет, взаимодействия нет.Я попробую компиляцию x86 и сообщу вам.

@ chibacity - поскольку это фоновая задача, она не замораживает пользовательский интерфейс.Вплоть до того момента, пока он не замерзнет, ​​индикатор выполнения и метка будут показывать примерно 2 каждую секунду.Когда он замерзает, он просто перестает двигаться.Я могу убедиться, что число, на котором он останавливается, было обработано, но не более.Использование процессора на двухъядерном процессоре 2,2 ГГц минимально во время работы: 3-4% каждый и 1-2% после его зависания.

@ Хенк Холтерман - до 1360 и около 10-12 минутда, я могу проверить, что все эти записи были обработаны, но не остальные записи.

@ CodeInChaos - Спасибо, я попробую!Код работает, если я уберу параллель, это займет вечность и день.Я не пробовал ограничивать количество потоков, но сделаю это.

РЕДАКТИРОВАТЬ 2:
Некоторые подробности о том, что происходит с веб-сервисами

В основном, чтоВеб-сервисы продолжают передавать некоторые данные и получать данные (XmlNode).Этот узел затем используется в процессе Conversion1, который, в свою очередь, устанавливает другое свойство объекта, которое отправляется в метод CallSvc2 и так далее.Это выглядит так:

private void CallSvc1(Entity entity)
{
    var svc = new MyWebService();
    var node = svc.CallMethod(entity.SomeProperty);
    entity.FieldToUpdate1.LoadXml(node.InnerXml);
}
private void Conversion1(Entity entity)
{
    // Do some xml inspection/conversion stuff
    if (entity.FieldToUpdate1.SelectSingleNode("SomeNode") == "something") {
        entity.FieldToUpdate2 = SomethingThatWasConverted;
    }
    else {
        // Do some more logic
    }
}
private void CallSvc2(Entity entity)
{
    var svc = new SomeOtherWebService();
    var xmlNode = svc.MethodToCall(entity.FieldToUpdate2.InnerXml);
    entity.AnotherXmlDocument.LoadXml(xmlNode.InnerXml);
}

Как видите, это довольно просто.В некоторых методах преобразования много чего происходит, но ни один из них не должен блокировать.И, как отмечено ниже, 1024 потока находятся в состоянии «ожидания», и все они находятся на вызовах веб-службы.Я прочитал здесь http://www.albahari.com/threading/, что MaxThreads по умолчанию установлен на 1023 для .Net 4 на 32-битной машине.

Как мне освободить эти ожидающие темы, учитывая то, что у меня здесь?

Ответы [ 2 ]

9 голосов
/ 03 декабря 2010

Возможное объяснение: вы получили процесс в состоянии, когда он больше не может создавать потоки, что препятствует продвижению работы, поэтому все останавливается.

Честно говоря, если эта гипотеза окажется верной, вам нужно принять совершенно другой подход к этому. Parallel.For - неправильный способ решить эту проблему. (Parallel лучше всего подходит для работы с процессором. Здесь у вас есть работа с IO.) Если вам действительно нужно выполнять тысячи запросов веб-сервисов, вам нужно перейти к использованию асинхронного кода вместо многопоточный код. Если вы используете асинхронные API, вы сможете запускать тысячи запросов одновременно, используя только несколько потоков.

Возможность выполнения этих запросов одновременно - это другой вопрос - используете ли вы текущую реализацию "апокалипсиса потока" или более эффективную асинхронную реализацию, возможно, вы столкнетесь с удушением. (.NET может иногда ограничивать количество запросов, которые он фактически делает.) Таким образом, вы можете попросить сделать столько запросов, сколько захотите, но вы можете обнаружить, что почти все ваши запросы находятся в ожидании завершения предыдущих. Например. Я думаю, что WebRequest ограничивает количество одновременных подключений к любому отдельному домену всего 2 ... Запуск 1000+ потоков (или 1000+ асинхронных запросов) просто приведет к загрузке большего количества запросов, ожидающих быть одним из 2 текущих запросов!

Вы должны сделать свое собственное регулирование. Вам необходимо решить, сколько ожидающих запросов иметь одновременно, и убедиться, что вы запускаете столько запросов одновременно. Просто попросив Parallel запустить столько, сколько он сможет, так быстро, как может, все погрязнет.

Обновлено для добавления:

Быстрое решение может заключаться в использовании перегрузки Parallel.For, которая принимает объект ParallelOptions - вы можете установить его свойство MaxDegreeOfParallelism, чтобы ограничить количество одновременных запросов. Это не позволит этой реализации, интенсивно использующей потоки, фактически исчерпать потоки. Но это остается неэффективным решением проблемы. (И, насколько я знаю, вам действительно нужно делать тысячи одновременных запросов. Например, если вы пишете веб-сканер, это действительно разумная вещь. Parallel - это не тот класс, который подходит для этого. работа, хотя. Используйте асинхронные операции. Если прокси веб-службы, которые вы используете, поддерживают APM (BeginXxx, EndXxx), вы можете обернуть это в Task объекты - Task.TaskFactory предлагает FromAsync, который обеспечит задачу представляет асинхронную операцию в процессе.

Но если вы собираетесь попытаться выполнить тысячи запросов одновременно, вам нужно тщательно продумать свою стратегию регулирования. Просто отбрасывать запросы как можно быстрее, вряд ли будет оптимальной стратегией.

5 голосов
/ 03 декабря 2010

Запустите приложение в отладчике VS. Когда кажется, что он заблокирован, скажите VS Debug: Break All. Затем перейдите в Debug: Windows: Threads и посмотрите на потоки в вашем процессе. Некоторые из них должны показывать трассировки стека, которые находятся в вашем параллельном цикле for, и это скажет вам, что они делали, когда отладчик остановил процесс.

...