Проблема связана с тем, как работает Parallel.ForEach
. Вы можете подумать, что он использует только фоновые потоки для выполнения работы, но на самом деле он также использует текущий поток. Другими словами, во время выполнения Parallel.ForEach
текущий поток играет роль рабочего потока. В вашем случае текущий поток - это поток пользовательского интерфейса. Условие if (progressBar1.InvokeRequired)
оценивается как true
для фоновых потоков, участвующих в операции, и false
для потока пользовательского интерфейса.
Фоновые потоки вызывают метод progressBar1.Invoke
в вашем примере. В отличие от BeginInvoke
, Invoke
является методом блокировки и возвращается только после того, как поток пользовательского интерфейса обработает предоставленный делегат. Поскольку поток пользовательского интерфейса занят обработкой своего собственного раздела коллекции files
, поток Invoke
будет заблокирован, поэтому все фоновые потоки застрянут, и единственный поток, который продолжит работу, будет потоком пользовательского интерфейса. В конце поток пользовательского интерфейса должен будет дождаться, пока другие потоки доставят результат единственного файла, который они получили изначально для обработки, что они не смогут сделать, поэтому Parallel.ForEach
зайдет в тупик. По крайней мере, это ожидаемый результат опубликованного вами кода. Поскольку вы не наблюдаете тупик, я предполагаю, что в вашем примере отсутствует какая-то строка кода (возможно, вызов Application.DoEvents
?), Которая разрешает ситуацию тупика.
Самый простой способ исправить это Неприятная ситуация заключается в том, что пользовательский интерфейс не может стать рабочим потоком. Просто используйте метод Task.Run
, чтобы выгрузить всю параллельную обработку в поток ThreadPool
:
await Task.Run(() =>
{
Parallel.ForEach(//...
});
Вам также нужно будет пометить свой обработчик событий с помощью async
ключевое слово, иначе компилятор не разрешит использование изящного оператора await
.
После применения этого исправления вы можете сделать свой код более элегантным, удалив все эти уродливые InvokeRequired
/ Invoke
и заменив его современным Progress
объектом. Это также тривиально упростило бы отделение logi c обработки файлов от logi c, относящегося к пользовательскому интерфейсу, если вы сочтете это желательным с архитектурной точки зрения. Вы можете прочитать эту статью, если хотите узнать, как использовать класс Progress
.