Sonhospa Ответов: 1

Действие задачи - кажется, у меня есть синтаксическая проблема


Привет,

Мне нужно перебрать большое количество файлов (обычно около 150 000 файлов ок. 48 МБ каждый) очень быстро и суммирует размеры файлов, а также количество файлов, показывая индикатор выполнения для пользователя.

Из образца MS я узнал эту параллель.ForEach кажется самым быстрым способом (пожалуйста, посмотрите на "что вы пробовали").

В byteCount начисляет штрафы, а результаты fileCount являются ложными. Вы наверняка поймете, что переменная 'fileCount' не заблокирована. К сожалению, я понятия не имею, как это сделать правильно, я сталкиваюсь с синтаксическими ошибками, что бы я ни пытался.

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

Спасибо,
Мик

Что я уже пробовал:

Parallel.ForEach(files, Function() 0, Function(f As FileInfo, loopState As ParallelLoopState, local_bytes As Long, local_files As Long)
    If (f.Attributes And FileAttributes.Directory) = 0 Then
        local_bytes += f.Length
    End If
    Return local_bytes
End Function, Sub(c)
    Interlocked.Add(total_bytes, c)   ' byteCount
    total_files += 1                  ' fileCount
    BeginInvoke(CType(Sub()
        pbVal = progBar.Value + increm
        If pbVal > 100 Then pbVal = 100
        progBar.Value = pbVal
   End Sub, Action))
End Sub)

Gerry Schmitz

Вся концепция передачи взаимосвязанных переменных "значения" в качестве параметров функции не имеет смысла. Вы создаете общий класс / метод, который каждый использует для доступа к "блокировке" или использованию блокировки, рассматриваемой переменной; вы не передаете ее по кругу.

1 Ответов

Рейтинг:
12

Richard Deeming

Попробуйте использовать Tuple для местного государства:

Parallel.ForEach(files, Function() (Length := 0L, Count := 0L), 
    Function(f As FileInfo, loopState As ParallelLoopState, index As Long, accumulator As (Length As Long, Count As Long))
        If (f.Attributes And FileAttributes.Directory) = 0 Then
            accumulator = (accumulator.Length + f.Length, accumulator.Count + 1)
        End If
        Return accumulator
    End Function,
    Sub(total As (Length As Long, Count As Long))
        Interlocked.Add(total_bytes, total.Length)
        Interlocked.Add(total_files, total.Count)
        BeginInvoke(CType(Sub()
            pbVal = progBar.Value + increm
            If pbVal > 100 Then pbVal = 100
            progBar.Value = pbVal
        End Sub, Action))
    End Sub)
Кортежи - Visual Basic | Microsoft Docs[^]


Sonhospa

Это прекрасно - спасибо, Ричард!

Sonhospa

Привет еще раз, извините, но у меня есть следующий вопрос... использование кода теперь показывает, что progressbar не работает. Сначала отображается результат (для теста я показываю его в окне сообщения), а после этого индикатор выполнения увеличивается. Можно ли вообще показать прогресс в параллельном цикле.foreach?

Richard Deeming

Это звучит так, как будто вы блокируете поток пользовательского интерфейса, ожидая Parallel.ForEach вызов для завершения. Вам, вероятно, нужно использовать BackgroundWorker[^] для выполнения работы в фоновом потоке, чтобы ваш пользовательский интерфейс имел возможность обновиться.

Sonhospa

Это вполне возможно... Я проверю и надеюсь решить эту проблему. Еще раз спасибо!

Sonhospa

Извини, что мне приходится просить тебя еще раз... Мне все еще не удавалось заставить его работать. Этот метод вызывается в таком обработчике кликов, который должен поместить его в фоновый поток:
Dim t As Task = Task.Run(Sub()
МСГ = Имя_метода(путь, "*.*", Системы.ИО.Searchoption указывает, нужно.AllDirectories)
Конец Подводной Лодки)
Т.Ждать()

Я установил точку останова в строке "progbar.value =" и заметил, что она достигается не для каждого файла, а через несколько произвольных интервалов, что делает расчет значения догадкой. Я оооочень застрял :-( что я действительно был бы рад еще одной хорошей идее от вас...

Richard Deeming

Task.Run(...).Wait() перемещает работу в фоновый поток, а затем блокирует текущий поток до тех пор, пока работа не будет завершена. Вы по-прежнему блокируете поток пользовательского интерфейса, но теперь одновременно используете поток пула потоков.

Попробуйте удалить его t.Wait() вызов. Или использовать async метод:

Private Sub ButtonClick(ByVal sender As Object, ByVal e As EventArgs)
    CountFilesAsync();
End Sub

Private Async Function CountFilesAsync() As Task
    Button.Enabled = False
    Try
        Await Task.Run(...)
    Finally
        Button.Enabled = True
    End Try
End Sub

Sonhospa

Спасибо вам за вашу помощь и идею, и хорошего вам уик-энда!