Как предотвратить зависание пользовательского интерфейса во время длительного процесса? - PullRequest
2 голосов
/ 15 марта 2010

Мне нужно написать апплет VB.Net 2008, чтобы пройтись по всем фиксированным дискам в поисках некоторых файлов. Если я поместил код в ButtonClick (), пользовательский интерфейс зависнет, пока код не будет завершен:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    'TODO Find way to avoid freezing UI while scanning fixed drives

    Dim drive As DriveInfo
    Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
    Dim filepath As String

    For Each drive In DriveInfo.GetDrives()
        If drive.DriveType = DriveType.Fixed Then
            filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
            For Each filepath In filelist
                'Do stuff
            Next filepath
        End If
    Next drive
End Sub

Google вернул информацию об элементе управления BackGroundWorker: это право / способ решить эту проблему? Если нет, какое решение вы бы порекомендовали, возможно, с очень простым примером?

FWIW, я читал, что Application.DoEvents () является остатком от VBClassic, и его следует избегать.

Спасибо.

Ответы [ 4 ]

5 голосов
/ 15 марта 2010

BackgroundWorker - хороший способ решить вашу проблему. На самом деле документация гласит:

Класс BackgroundWorker позволяет запускать операцию в отдельном выделенном потоке. Отнимающие много времени операции, такие как загрузка и транзакции с базой данных, могут привести к тому, что пользовательский интерфейс (UI) будет выглядеть так, как будто он перестал отвечать во время работы. Если вам нужен отзывчивый пользовательский интерфейс и вы сталкиваетесь с длительными задержками, связанными с такими операциями, класс BackgroundWorker предоставляет удобное решение.

2 голосов
/ 15 марта 2010

Ключ состоит в том, чтобы отделить код пользовательского интерфейса от фактического кода функциональности. Отнимающая много времени функциональность должна выполняться в отдельном потоке. Для этого вы можете:

  1. Создание и запуск Thread объекта с помощью себя
  2. Создайте Delegate и используйте асинхронный вызов (используя BeginInvoke).
  3. Создать и запустить BackgroundWorker.

Как вы упомянули, вам следует избегать Application.DoEvents(). Правильная разбивка функциональности приложения позволит вам создать приложение, которое разработано так, чтобы быть отзывчивым, а не создавать неотвечающее приложение с «исправлениями» DoEvents (что дорого, считается плохой практикой и подразумевает плохой дизайн).

Поскольку ваш метод не возвращает значение и не обновляет пользовательский интерфейс, самым быстрым решением может быть создание делегата и использование асинхронного вызова «запусти и забудь»:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Call New Action(AddressOf DrivesIteration).BeginInvoke(Nothing, Nothing)
End Sub

Private Sub DrivesIteration()
    Dim drive As DriveInfo
    Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
    Dim filepath As String

    For Each drive In DriveInfo.GetDrives()
        If drive.DriveType = DriveType.Fixed Then
            filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
            For Each filepath In filelist
                DoStuff(...)
            Next
        End If
    Next 
End Sub

Кстати, For..Next блоки больше не должны заканчиваться на «Next (что-то)», они устарели - VB теперь выводит (что-то) само по себе, поэтому нет необходимости указывать это явно.

2 голосов
/ 15 марта 2010

Поместите процесс в отдельный поток .... ... используя компонент BackgroundWorker.

Отключить компоненты пользовательского интерфейса, которые не должны использоваться во время работы процесса.

Закончено - пользовательский интерфейс по-прежнему будет отзывчивым.

0 голосов
/ 15 марта 2010

A. поднимите PROGRESS BAR ... обновите его и .REFRESH ... Если все, что вам нужно, это показать, что вы не мертвы.

B. DoEvents - это злые звуки, такие как «НИКОГДА НЕ ИСПОЛЬЗУЙТЕ GOTO ...» pleeeeze pleeeze pleeeze Времена и обстоятельства, когда синтаксис любого языка может быть вредным и полезным. Зачем прыгать через миллион обручей, чтобы по существу сделать «А» выше?

<<em> мыльница >

Если вы знаете, что что-то занимает ДЛИННОЕ ВРЕМЯ, и вы также знаете, что никакие другие операции не могут выполняться во время вашего ожидания (т. Е. Это, по сути, последовательный процесс), чем если вы делаете НИЧЕГО, как это, и толкаете это в «фон» тогда вы будете выдавать «ITS_OK_TO_CONTINUE» логические значения во всем остальном коде, просто ожидая окончания файлового процесса в любом случае… в чем смысл? Все, что вы сделали, это усложнили свой код ради ... хм ... "хорошего программирования?" Не в моей книге.

Кому какое дело, если DoEvents «остался» от ICE AGE. Это ТОЧНО правильно в МНОГИХ обстоятельствах. Например: фреймворк предоставляет вам ProgressBar.Refresh , но вы увидите, что он не совсем "работает", если вы не отложите несколько DoEvents после него.

<<em> / мыльница >

C. Фоновая задача - это просто фон; и вы обычно используете его для работы с задачами НЕ-SERIAL или, по крайней мере, с асинхронными задачами, которые МОГУТ или МОГУТ НЕ обновлять передний план в какой-то момент. Но я бы сказал, что всякий раз, когда так называемая фоновая задача ОСТАНАВЛИВАЕТ передний план, тогда она (почти) по определению --- ЗАПРОЩЕННАЯ задача; независимо от того, КАК ДОЛГО это займет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...