Как определить причину, по которой основной поток не отвечает с помощью библиотеки Omni Thread?

Платформа: Delphi с VirtualTreeView SVN 5.1.0 и OmniThreadLibrary 3 SVN и Delphi XE2

Изначально я думал, что проблема в VirtualTreeView. Мне нужно добавлять узел в VST каждую 1 секунду или реже. Но, похоже, рано или поздно скорость ЦП достигнет 50% или более, пока все приложение полностью не перестанет отвечать.

  var FAbort:Boolean;
  .....

  procedure TrmMain.btnAddNodeClick(Sender: TObject);
    begin
      while not FAbort do
      begin
        VstMain.RootNodeCount:= VstMain.RootNodeCount + 1;
        Sleep(10);
        Application.ProcessMessages;
      end;
    end;

Кто-нибудь может помочь? ТИА!

РЕДАКТИРОВАНИЕ: похоже, проблема связана с OTL. При использовании приведенного выше кода минимизируйте нагрузку ЦП приложения всегда менее чем на 1%, даже измените спящий режим с 10 мс на 1 мс.

Но приведенный ниже код воспроизводит проблему, которая меня беспокоит.

procedure TForm1.btn5Click(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to 1 do
    CreateTask(
      procedure(const Task: IOmniTask)
      begin
        while not FAbort do
        begin
          Task.Comm.Send(1, 0);
          Sleep(10);
        end;
      end).OnMessage(
      procedure(const Task: IOmniTaskControl; const Msg: TOmniMessage)
      begin
        vst1.AddChild(nil);
      end).Run;
end;

PS: Чтобы избежать флуда до размера очереди OTL по умолчанию 1000, у меня ДЕЙСТВИТЕЛЬНО есть блокировка в каждом потоке, который ожидает завершения добавления узла перед следующей операцией Task.Comm.Send.

PPS: 10 мс здесь только для быстрого воспроизведения проблемы, а не в реальной ситуации. Так что не трудитесь спрашивать, почему?

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


person XARA    schedule 10.01.2013    source источник
comment
Не могли бы вы добавить больше зла? Бесконечный цикл во время сна, плюс Application.ProcessMessages? Это симфония Плохого. Как насчет добавления нарушений прав доступа и утечек памяти? Шутки в сторону. О чем ты думал? Добавьте TTimer и добавьте узел из таймера, а не из цикла. Приложения с графическим интерфейсом не зависят от вас с помощью бесконечных циклов. Научитесь любить программирование, управляемое событиями, и поймите, что Sleep(X) и Application.ProcessMessages не помогут решить ваши проблемы.   -  person Warren P    schedule 10.01.2013
comment
Будьте здоровы, я просто пытаюсь воспроизвести проблему, реальный код не такой, как тот, что я привел выше. Событие добавления узла произошло только тогда, когда появился новый журнал.   -  person XARA    schedule 10.01.2013
comment
Подключите профайлер и узнайте, на что он тратит свое время. Помните, что внутренняя структура данных представляет собой двусвязный список, поэтому его обход занимает больше времени по мере роста структуры.   -  person Rob Kennedy    schedule 10.01.2013
comment
Довольно сложно понять проблему, если вы описываете поведение, а затем публикуете поддельный код, а не настоящий код.   -  person David Heffernan    schedule 10.01.2013
comment
почему вы должны добавлять узел каждые 10 мс? кто может читать каждые 10 мс новую запись?   -  person Sir Rufo    schedule 10.01.2013
comment
Вы не можете сказать, что изолировали свою проблему, если можете воспроизвести ее, используя только приведенный выше вредоносный код, но приведенный выше вредоносный код не является вашей настоящей проблемой. Используйте настоящий профилировщик (AQTime), и вы будете знать, вместо того, чтобы играть в игры.   -  person Warren P    schedule 10.01.2013
comment
Я не понимаю связи минимизации приложения с потоками. Вы думаете, что многопоточность — это проблема, потому что при сворачивании приложение ведет себя по-другому?   -  person Sertac Akyuz    schedule 10.01.2013
comment
Название вашего вопроса ужасно, так как вы обнаружили, что это не имеет ничего общего с VirtualTreeView. Я не понимаю, как ваш не-вопрос может превратиться в настоящий вопрос. Как насчет его удаления? Я отредактировал ваш заголовок, удалив ссылку на virtualtreeview, поскольку она на самом деле не применима, но ваш вопрос по-прежнему просит нас отладить вашу гигантскую кучу невидимого кода, и поэтому я голосую за закрытие.   -  person Warren P    schedule 10.01.2013
comment
@Warren P: Спасибо за помощь. Я кодирую приложение, которое нужно запускать в фоновом режиме 24 часа, частота добавления узла не такая экстремальная, как в демо, которое я загрузил на RapidShare. Но, после нескольких часов работы, в конечном итоге приложение будет заморожено.   -  person XARA    schedule 11.01.2013
comment
@Sertac Akyuz: после свертывания приложения ЕСТЬ разница. Для лучшего ощущения загрузите мой EVIL CODE, который я загрузил на RapidShare. :D   -  person XARA    schedule 11.01.2013
comment
@XARA - я не говорил, что нет никакой разницы. Я спросил, почему эта разница должна быть вызвана OTL.   -  person Sertac Akyuz    schedule 11.01.2013
comment
@Sertac Akyuz: это просто для лучшего представления о повышении скорости процессора, избегая обновления пользовательского интерфейса.   -  person XARA    schedule 11.01.2013
comment
@XARA - Ах! Я неправильно понял эту часть вопроса. Спасибо, что прояснили.   -  person Sertac Akyuz    schedule 11.01.2013


Ответы (2)


На мой взгляд, вы не должны синхронно обновлять представление при изменении базовой модели, особенно не каждый раз.

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

Вместо этого обновите свою модель (объекты, классы), установите флаг уведомления, а затем (из TTimer) выполните асинхронное ОДНОКРАТНОЕ обновление VirtualStringTree.RootNodeCount, которое происходит максимум 3 раза в секунду. (Более одной установки этого флага обновления каждые 333 мс приводит к минимальному периоду ожидания в 333 мс, пока он действительно не обновится.) Это мой произвольный пользовательский интерфейс «быстрее, чем это, и он просто мерцает и взбалтывается, и вообще бесполезен». .

Собственные разработчики Delphi столкнулись с этим с VirtualTreeView, я знаю, потому что я зарегистрировал связанную с этим ошибку QC. Если вы сделали достаточно сообщений "OutputDebugString" в Delphi 2009, IDE перестанет отвечать. Почему? Потому что они сделали то, что делаете вы. Не делай этого. Я не говорю, что щелчок пользователя должен вызывать ожидание в 333 мс, прежде чем экран обновится. Я говорю, что некоторый процесс, который постоянно генерирует содержимое дерева, должен уведомлять «контроллер представления» дерева об изменениях максимум 3 раза в секунду.

person Warren P    schedule 11.01.2013
comment
П: Спасибо, я понял вашу мысль. Представление журнала Delphi IDE действительно использует VST, и у него ДЕЙСТВИТЕЛЬНО есть эта проблема. Андреас Хаусладен, автор пакета исправлений IDE, добавил таймер с интервалом 70 мс 0 или 80 мс для обновления VST и устранил эту проблему. Если дочерние узлы одного узла (в моем случае RootNode) растут слишком быстро и слишком сильно, то накладные расходы на обновление пользовательского интерфейса каждый раз становятся слишком дорогими. В моем коде я просто добавляю ограничение, равное 10 000, если дочерние узлы превышают его, сохраняйте его, прежде чем очистить их все, наконец, это решило мою проблему. Как вы упомянули, периодическое добавление множества из них намного эффективнее . - person XARA; 12.01.2013
comment
Извините, я не заставил @ работать. когда я набираю твое имя, оно просто съедает его. - person XARA; 12.01.2013
comment
Если это решило вашу проблему, вам следует отредактировать заголовок и вопрос, чтобы их было легче найти будущим людям. - person Warren P; 13.01.2013

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

Например что-то вроде:

procedure TMyForm.OnTimer( Sender: TObject );
var
  Node: PVirtualNode;
begin
  Node := MyTree.AddChild( nil );
  // fill in details with GetNodeData( Node );

end;

А еще лучше: используйте таймер и попробуйте добавить несколько элементов за временной интервал:

procedure TMyForm.OnTimer( Sender: TObject );
begin
  AddToList( ... );
end;

procedure TMyForm.OnTimer( Sender: TObject );
var
  Node: PVirtualNode;
  Item: <Some iterator>;
begin
  MyTree.BeginUpdate();
  try
    for Item in <somelist> do begin
      Node := MyTree.AddChild( nil );
      // fill in details with GetNodeData( Node );
    end;
  finally
    MyTree.EndUpdate();
  end; 
end;
person Ritsaert Hornstra    schedule 10.01.2013
comment
Я согласен с тем, что расширение RootNodeCount 100 раз в секунду при значениях RootNodeCount > 1000 может стать все более дорогостоящим. Может произойти какая-то безумная перестройка всего дерева. Но единственный способ узнать это — наш умный оригинальный пост с просьбой запустить AQTime и выяснить это самому. - person Warren P; 10.01.2013