2010-12-02 1 views
10

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

Я ничего не пишу на диск или не читаю с диска.

Я также обновляю пользовательский интерфейс в приложении 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)?

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

EDIT:
Некоторые ответы на комментарии ниже:
@BrokenGlass - Нет, нет взаимодействия с другими программами. Я попробую компиляцию x86 и дам вам знать.

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

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

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

EDIT 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 потока, которые все сидят на вызовах webservice. Я прочитал здесь http://www.albahari.com/threading/, что MaxThreads по умолчанию имеет значение 1023 для .Net 4 на 32-битной машине.

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

+0

У меня была аналогичная проблема раньше - я бы попытался создать проект в режиме x86, чтобы увидеть, что это что-то изменит. Вы не могли бы сделать какой-либо InterOp в своих задачах? – BrokenGlass 2010-12-02 21:41:54

+0

Замерзает или идет очень медленно. Как используется ЦП? – 2010-12-02 21:42:24

+0

Не работали с TPL еще, но не можете ли вы просто сломать в отладчике и проверить, какой метод вызывает функции остановки? И работает ли код, если вы замените его на обычный цикл? И что произойдет, если вы используете `Parallel.For`, но ограничиваете его одним или двумя потоками? – CodesInChaos 2010-12-02 21:51:07

ответ

9

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

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

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

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

Обновлено добавить:

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

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

5

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

Смежные вопросы