2016-01-15 2 views
2

Я использовал Delphi много лет назад, я помню, что в версии 7, и даже тогда у меня не было много знаний, я работал с C++ с тех пор, и сейчас я работаю в компании, использующей Delphi XE10 и понял, что поиск я нашел много случайных вещей разностных версий, и это меня немного смущает.Обновление темы GUI

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

Мне было интересно, есть ли более плавный способ справиться с этим, что-то вроде событий, уведомлений или чего-то еще?

@edit

unit WriterThreadUnit; 

interface 

uses 
    System.Classes, System.SysUtils, Unit1; 

type 
    TWriterThread = class(TThread) 
    private 
    linesToPrint: integer; 
    fileDirectory: string; 
    function generateFilename(): string; 
    procedure write(); 
    protected 
    procedure Execute; override; 
    public 
    constructor Create 
    (
     const createSuspended: boolean; 
     const linesToPrint: integer; 
     fileDirectory: string 
    ); 
    end; 

implementation 

{ TWriterThread } 

constructor TWriterThread.Create 
(
    const createSuspended: boolean; 
    const linesToPrint: integer; 
    fileDirectory: string 
); 
begin 
    Self.linesToPrint := linesToPrint; 
    Self.fileDirectory := fileDirectory; 
    Self.FreeOnTerminate := true; 

    inherited Create(CreateSuspended); 
end; 

procedure TWriterThread.Execute; 
begin 
    inherited; 

    write; 
end; 

function TWriterThread.generateFilename: string; 
begin 
    Result := Format('%s\%s_total_lines_%d.txt', 
    [ 
     Self.fileDirectory, 
     FormatDateTime('hh-mm-ss-zzz', Now), 
     self.linesToPrint 
    ] 
); 
end; 

procedure TWriterThread.write; 
var 
    fileLines: TStringList; 
    i: integer; 
    filename: string; 
begin 
    fileLines := TStringList.Create; 
    filename := generateFilename; 

    try 
    for I := 1 to Self.linesToPrint do 
    begin 

     Synchronize(
     procedure 
     begin 
      Form1.Memo1.Lines.Add('Writing: ' + IntToStr(I) + ' to ' + generateFilename); 
     end 
    ); 

     fileLines.Add(Format('Line number: %d', [i])); 
     Sleep(1); 
    end; 

    fileLines.SaveToFile(filename); 
    finally 
    fileLines.Free; 
    end; 
end; 

end. 
+0

Возможно, вы используете 'Synchronize' неправильно. Процедура «Синхронизированная» должна работать немного - просто обновите интерфейс в основном потоке. Покажите вам код, пожалуйста. Конечно, есть много других способов, с событиями и т. Д. – MBo

+0

@MBo Отредактировано кодом потока. – daitouvi

+0

Мне любопытно, почему вы обновляете заметку с каждой итерацией? Это делает поток бессмысленным .. держите поток в фоновом режиме и обновляйте как можно меньше. –

ответ

2

Ваш код не так - он просто тратит много времени на много, много мелких обновлений VCL GUI.

Я подозреваю, что перемотка Memo не является быстрой процедурой (примечание BeginUpdate/EndUpdate влияние, когда Memo заполняется тысячами строк по строке). Поэтому сделайте вызовы синхронизации как можно реже (при необходимости отправляя пакеты строк). Действительно, пользователь не способен видеть 1000 обновлений в секунду.

+0

Я не знаю почему, но я все еще думаю, что 'Synchronize' заставляет приложение замораживать. Есть ли другая альтернатива? – daitouvi

+0

Я прошу альтернативу, потому что представьте, если я выполняю какой-то процесс, например, используя поток, и мне нужно обновить индикатор выполнения или что-то еще, я не думаю, что 'Synchronize' поможет много. Я попробовал образцы, которые поставляются с Delphi, и есть один для загрузки HTTP, и он замораживает графический интерфейс, это заметно. – daitouvi

+0

Проблема в том, что вы вызываете синхронизацию для каждого цикла цикла. Это может в зависимости от остальной части цикла вашего цикла привести к более чем 1000 вызовам Synchronize для обновления пользовательского интерфейса вашего приложения. Но поскольку обновление пользовательского интерфейса замедляет работу, это создает узкие места для ваших приложений.На самом деле, даже если обновление пользовательского интерфейса будет намного быстрее, пытаясь обновить его чаще, что ваш экран может обновиться, это просто трата ресурсов обработки. Поэтому попробуйте уменьшить количество раз, когда синхронизация вызывается, вызывая ее каждые 10 или, возможно, даже лучше каждые 100 циклов цикла. – SilverWarior

2

Почему бы вам просто не исправить свой текущий код, как говорили другие? Вы не должны обновлять GUI тысячи раз в секунду, вы понимаете, что это не имеет никакого смысла? Однако даже ваш пример работает без замораживания GUI на моей машине (и моя машина устарела).

Вот пример кода, как ускорить процесс.

procedure TWriterThread.write; 
var 
    fileLines, memoLines: TStringList; 
    i: integer; 
    filename: string; 
    procedure InternalSynchronize; 
    begin 
    Synchronize(
     procedure 
     begin 
     Form1.Memo1.Lines.BeginUpdate; 
     Form1.Memo1.Lines.AddStrings(memoLines); 
     SendMessage(Form1.Memo1.Handle, EM_LINESCROLL, 0, Form1.Memo1.Lines.Count); //used to scroll down the memo 
     Form1.Memo1.Lines.EndUpdate; 
     end 
    ); 
    end; 
begin 
    fileLines := TStringList.Create; 
    memoLines := TStringList.Create; 
    filename := generateFilename; 

    try 
    for I := 1 to Self.linesToPrint do 
    begin 

     if i mod 50 = 0 then //Update GUI one per 50 writes. 
     InternalSynchronize; 

     memoLines.Clear; 
     memoLines.Add('Writing: ' + IntToStr(I) + ' to ' + generateFilename); 
     fileLines.Add(Format('Line number: %d', [i])); 
    end; 

    InternalSynchronize; 
    fileLines.SaveToFile(filename); 
    finally 
    fileLines.Free; 
    memoLines.Clear; 
    end; 
end; 

КСТАТИ: если Memo1 мерцает во время обновления, вы можете рассмотреть возможность использования DoubleBuffered собственности на форме.

+2

Не используйте DoubleBuffered для бумаги поверх трещин, исправьте мерцание. –

0

Лично я думаю, вам следует «ограничить максимальную FPS», как в 3D-играх.

Я думаю, вы не пытаетесь показать пользователю какие-то плавные формы, плавно переходящие друг в друга, вместо этого вы хотите, чтобы он увидел какую-то текстовую информацию.

Затем вы должны предоставить пользователю время для просмотра и чтения текста. Если вы этого не сделаете - тогда нет смысла даже показывать его.

Так что я думаю, вы должны

  1. Набор TTimer на форме, которая будет обновлять несколько раз в секунду. По моему опыту с простым текстом, например «12,5% выполненной работы» или «123 ячейки обработанного 4321», частота должна составлять от 2 до 4 повторных визуализации в секунду (TTimer задержка между 250 и 500 мс). Чем сложнее ваш выход - тем больше времени потребуется человеку для чтения, тем меньше должна быть частота.

  2. Нити должны излучать данные, которые будут визуализированы в специальный буфер с несколькими сценариями/одним считывателем. Для простых текстов, как я изложил выше, сообщения Windows достаточно хорошо (просто не забывайте, что ваши потоки должны использовать PostMessage никогда SendMessage).Для более сложных данных, я предлагаю вам использовать iOmniBlockingCollection из OmniThreads Library - http://otl.17slon.com

  3. Вы TTimer события должны проверять наличие новых данных, делаю это так быстро, как это возможно - с помощью TStrings.BeginUpdate на TMemo.Lines действительно имеет значение - и удалить из буфера.

  4. Когда процесс завершен, вы останавливаете TTimer и не публикуете данные о состоянии/форме заказа.

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

Смотрите также мои ответы на

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