#delphi
#delphi
Вопрос:
У меня есть программа, которой время от времени требуется рекурсивное сканирование некоторых каталогов (улучшение для этой части программы находится в стадии разработки, но некоторое время не будет готово). Чтобы пользователю не приходилось ждать этого сканирования, я хотел бы выполнять сканирование, пока пользователь не использует мою программу, когда это возможно.
Я намерен реализовать это, запустив таймер, который проверяет время простоя. Я нашел следующее для проверки времени простоя системы: http://www.delphitips.net/2007/11/11/how-to-detect-system-idle-time /
Это было бы функционально, но я бы предпочел активировать функцию, даже когда пользователь работает с другими программами на том же компьютере. Таким образом, если он переключится на другую программу, я мог бы завершить сканирование, которое мне нужно выполнить.
Я понимаю, что я мог бы выполнить сканирование в фоновом потоке, и либо это, либо какие-то перехваты Windows будут реализованы в какой-то момент, но не сейчас.
РЕДАКТИРОВАТЬ: Цель здесь — относительно простое изменение в программе для выполнения любого сканирования, которое может быть поставлено в очередь, пока пользователь активно не использует МОЕ приложение. Сканирование не особенно интенсивное, но оно выполняется не в потоке, и поэтому мое приложение зависает во время выполнения. В принципе, я ищу быструю победу, пока работаю над более долгосрочным решением.
Комментарии:
1. Потоку было бы все равно, простаивало ли приложение, и это не помешало бы им использовать приложение?
2. Не могли бы вы не использовать вызов OnIdle в TApplication? Вы могли бы увеличивать счетчик для каждого вызова OnIdle и сбрасывать его каждые 1 секунду. Если оно достигает «величины», вы идете делать свое дело.
3. Он мог использовать OnIdle, @Brian, но не так, как вы предложили. Событие работает не так.
4. Начните прямо с того, что поместите это в фоновый поток. Это будет проще реализовать, чем реагировать на события. Кроме того, что вы сканируете? Существует несколько функций win api, которые будут предупреждать вас об изменении в каталоге, что может значительно упростить выполнение сканирования.
Ответ №1:
Используйте Application.OnIdle
событие. Это событие запускается, когда вашей программе больше не нужно обрабатывать оконные сообщения. Клавиатура и мышь генерируют сообщения, поэтому, если сообщений больше нет, пользователь не использует вашу программу, даже если она имеет фокус.
procedure TJasonForm.ApplicationEventsIdle(Sender: TObject; var Done: Boolean);
var
NoMoreFiles: Boolean;
begin
// TODO: Scan for the next file
Done := NoMoreFiles;
end;
Пока Done
равно False и в очереди нет сообщений, ваша программа будет продолжать вызывать этот обработчик событий, позволяя вам находить другие файлы. Когда пользователь генерирует некоторые входные данные, ваша программа обработает сообщения перед повторным вызовом OnIdle
.
Когда вы закончите сканирование файловой системы, установите Done
значение True, чтобы программа прекратила повторный вызов OnIdle
обработчика. Если вы пренебрегаете этим, то ваша программа будет использовать все доступное процессорное время, неоднократно вызывая обработчик событий, который ничего не делает.
Чтобы это сработало, вам нужно будет использовать нерекурсивную процедуру поиска. Рекурсивный поиск перед возвратом выполнит поиск по всей файловой системе, но если вы это сделаете, то OnIdle
обработчик зависнет в вашей программе, что противоречит тому, что вы хотите. Вы можете использовать очередь имен каталогов, и каждый раз, когда срабатывает событие, извлекать один элемент из очереди, выполнять поиск по нему и добавлять его содержимое в конец очереди.
Комментарии:
1. Помните, что даже простое перемещение указателя мыши в окне вашего приложения отправит вашему приложению оконные сообщения для его обработки. Пользователь не использует ваше приложение как таковое. Возможно, вы захотите принять это во внимание.
2. Это выглядит интересно, но у меня ограниченные возможности для прерывания поиска. Это было бы идеально, если бы я мог добавить какое-то ожидание. Возможно, я мог бы активировать таймер из функции OnIdle? Тогда было бы более вероятно, что пользователь действительно простаивает, а не просто перестал перемещать мышь на мгновение.
3. Где таймер, конечно, проверил бы, что программа все еще простаивает, а затем запустил бы сканирование.
4. Да, вы, вероятно, могли бы это сделать. Установите состояние в «idle» при получении события OnIdle; установите его в «not idle» при получении события onMessage.
5. И если я захочу выполнить повторное сканирование через некоторое время, как я могу получить OnIdle, если я установлю для Done значение true?
Ответ №2:
…но я бы предпочел активировать функцию, даже если пользователь работает с другими программами на том же компьютере. Таким образом, если он переключится на другую программу, я мог бы завершить сканирование, которое мне нужно выполнить.
Остановите сканирование в приложении.Активировать и возобновить в приложении.OnDeactivate.
Комментарии:
1. Это также полезно. Если бы я объединил это с моей версией решения Роба, я мог бы довольно эффективно запускать сканирование всякий раз, когда пользователь отключается от моего приложения или простаивает во время использования моего приложения.
Ответ №3:
Что вам нужно, так это отдельный поток, который будет возобновлен, когда система простаивает, или приостановлен, когда есть активность. Простой способ сделать это — поместить компонент Timer и проверить, находится ли система в режиме ожидания, а поток приостановлен, или не использовать ResumeThread ,SuspendThread
Смотрите здесь
http://pastebin.com/8X9Sg42H я создал кое-что для вашей ситуации, наслаждайтесь
Комментарии:
1. Если это отдельный поток, установите его приоритет на «idle» и позвольте ОС позаботиться о его планировании автоматически.
2. Поток с низким приоритетом, который использует ввод-вывод, может задержать поток с более высоким приоритетом, если после начала ввода-вывода потоку с более высоким приоритетом приходится ждать завершения ввода-вывода, прежде чем продолжить. Это, скорее всего, произойдет, если поток с низким приоритетом сканирует файлы.
3. Не используйте suspend / resume для управления выполнением потока, просто не делайте этого, это слишком опасно. Сделайте это правильно. Создайте поток при запуске приложения. Не завершайте его и не используйте TThread.waitFor / Synchronize / onTerminate, просто выполните цикл вокруг вызова ‘ReadDirectoryChangesW’, поместите результаты в экземпляр TStringList и отправьте сообщение в элемент управления / форму. Очень старайтесь не использовать таймер / OnIdle bodge. Обязательно установите приоритет ниже, как предложил Роб.
4. У меня никогда не было проблем с использованием приостановки и возобновления потока, но вы можете быть правы, если поток выполняет сложные операции
5. @MartinJames Не уверен, почему вы считаете, что приостановка / возобновление слишком опасно. Вы должны учитывать, что приостановка может произойти в любое время , поэтому очевидно, что если поток получит какую-либо блокировку, разделяемую другим потоком, то ваше приложение перейдет в тупик, но помимо этого, в чем опасность?