Перейти к содержанию
Fire Monkey от А до Я
  • 0

Кто хорошо знает внутреннюю структуру FMX?


Akad

Вопрос

С недавних пор (студия 10.2.2) программа начала падать в System.Classes в function CheckSynchronize. Конкретно кусок



            try
              if Assigned(SyncProc.SyncRec.FMethod) then
                SyncProc.SyncRec.FMethod()
              else if Assigned(SyncProc.SyncRec.FProcedure) then
                SyncProc.SyncRec.FProcedure();
            except
              if not SyncProc.Queued then
                SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject
              else if Assigned(ApplicationHandleException) then
                ApplicationHandleException(SyncProc.SyncRec.FThread);
            end;

И далее. Дело в том, что в SyncProc.SyncRec или nil или мусор. Ни в стеке вызовов, ни в других нитках я намёков на причину не нашёл. В стеке вызов CallNextHookEx, который дёргает StdWndProc, а та пытается синхронизироваться. Последний ассерт происходит из-за Error(reMonitorNotLocked) в

function TMonitor.CheckOwningThread: TThreadID;

и далее программа или работает нормально, или повисает UI намертво - когда как.  Подозреваю, что пытается синхронизироваться объект, которого уже нет.  У меня все формы и все компоненты на них создаются и удаляются динамически. Таблицы заполняются в других потоках через Synchronize и т.д  В общем проект большой, и что иголку в стоге сена искать... Глюк плавающий, но проявляется наиболее стабильно после закрытия определённого окна.

Создаю компоненты стандартно. Типа:

control := TEdit.Create(parent);

и удаляю тоже

if assigned(control) then FreeAndNil(control);

Форму создаю:

      BaseForm := TEmtyForm_frm.Create(Application);
      BaseForm.Name := 'Form'+IntToStr(Random(256)*Random(256));
      if assigned(parentScRun) then
         BaseForm.Parent := parentScRun.BaseForm;
удаляю соответственно:

    if assigned(BaseForm) then FreeAndNil(BaseForm);

Посоветуйте куда "копать"? Может ли быть верна моя догадка, что это из-за уже разрушенного объекта? Если да - что надо синхронизировать перед  FreeAndNil? Если нет - что ещё посмотреть?

 

Изменено пользователем Akad
Ссылка на комментарий

Рекомендуемые сообщения

  • 0

Если ты сам руками удаляешь компоненты, то правильнее их будет создавать так:

control := TEdit.Create(nil);

control.Parent := parent;

уничтожаю контролы через .DisposeOf (но с таким способом уничтожения согласны не все)

Ссылка на комментарий
  • 0
В 08.02.2018 в 16:27, Akad сказал:

Заменил код. Сделал ребилд. Упало в этом-же месте в CheckSynchronize.
 

Тоже самое, один в один. Причем проект на VCL. Та же самая ошибка была в ранних версиях XE. Потом ее пофиксили. 

В моем случае помогло (надеюсь. пока еще тестирую) избавление от всех TThread.Synchronize в проекте. Заменил на обмен через TThreadedQueue. Теперь до строк 

 if Assigned(SyncProc.SyncRec.FMethod) then
 	SyncProc.SyncRec.FMethod()
else if Assigned(SyncProc.SyncRec.FProcedure) then
    SyncProc.SyncRec.FProcedure();

вообще не доходит слава богу.

Ссылка на комментарий
  • 0
В 08.02.2018 в 16:57, Akad сказал:

if assigned(BaseForm) then FreeAndNil(BaseForm);

мой вариант

 BaseForm := TEmtyForm_frm.Create(NIL);
...
freeandnil(BaseForm);

вызывает вопросы назначение Form.parent := 

если вам нужно вставить что-то куда-то, гораздо лучше это делать с фреймами

в этом случае

frm := TMyFrame.Create(NIL);
frm.parent := panel1;
...
frm.parent := nil;
freeandnil(frm);

 

Ссылка на комментарий
  • 0

Есть дикое ощущение, что могли поломать синхронизацию через TMonitor.Wait.

В Телеграме обсуждали подобный глюк, по stacktrace было похоже на это.

Ссылка на комментарий
  • 0
1 час назад, kami сказал:

Есть дикое ощущение, что могли поломать синхронизацию через TMonitor.Wait.

В Телеграме обсуждали подобный глюк, по stacktrace было похоже на это.

После переделки с TThread.Synchronize на TThreadedQueue приложение отработало 12 часов без проблем. Ни ошибок, ни утечек. Так что думаю проблема не в моем коде, а именно в TMonitor. 

Правда на тестовом приложении, с 7 потоками и синхронизацией TThread.Synchronize воспроизвести проблему не удалось, так что черт его знает на каком этапе начинает глючить.

Ссылка на комментарий
  • 0
11 час назад, Евгений Корепов сказал:

После переделки с TThread.Synchronize на TThreadedQueue приложение отработало 12 часов без проблем. Ни ошибок, ни утечек. Так что думаю проблема не в моем коде, а именно в TMonitor. 

Правда на тестовом приложении, с 7 потоками и синхронизацией TThread.Synchronize воспроизвести проблему не удалось, так что черт его знает на каком этапе начинает глючить.

Вы бы хоть код выложили с TThreadedQueue, как использовать вместо TThread.Synchronize, чтобы сообщество было  в курсе. Спасибо!

Ссылка на комментарий
  • 0

Очень похоже, что дело в синхронайзе. Для системы окон он неожидаднен. Чуть позже отпишусь, что TThreadedQueue, или какой ещё вариант лечит косяк с возвращением потока в GUI.

 

Изменено пользователем Akad
Ссылка на комментарий
  • 0
20 часов назад, Brovin Yaroslav сказал:

Где удаляете форму?

В главном потоке, после удаления всех элементов на ней. В общем точно проблема в Synchronize, который я использую на той форме, которая в текущий момент активна, а не той, которая разрушена (после закрытия дочерней формы стразу стартует от 3 до 5 потоков, которые лезут в интернет и заполняют результатом этого действия таблицу).

Вопрос что с этим делать. Фактически мне надо оказаться в главном потоке для TGrid.BeginUpdate/TGrid.EndUpdate. Всему остальному работать в GUI не обязательно. 
 

Ссылка на комментарий
  • 0
В 19.02.2018 в 22:43, wamaco сказал:

Вы бы хоть код выложили с TThreadedQueue, как использовать вместо TThread.Synchronize, чтобы сообщество было  в курсе. Спасибо!

Вот простой пример накидал. Главная форма, поток, две очереди (очередь запросов и очередь ответов). Вы в главной форме вбивате два числа, и нажимаете кнопку по своему усмотрению (прибавить, умножить, разделить). Числа отправляются в Очередь запросов, поток получает задачу, выполняет и отправляет ответ в очередь ответов. Главная форма, в Application.OnIdle ждет получения результатов проверяя Очередь ответов, при получении добавляет их в Memo. Все. Проект прилагаю в архиве, вот листинг:

unit UnitFormMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  System.Generics.Collections, FMX.StdCtrls, FMX.Edit, FMX.EditBox,
  FMX.NumberBox, FMX.Layouts, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo;

type
  TThreadOperation = (Sum, Mult, Divinity);
  TThreadDataRequest = record
    A : Double;
    B : Double;
    Operation : TThreadOperation;
  end;
  TThreadDataAnswer = record
    Operation : TThreadOperation;
    A : Double;
    B : Double;
    X : Double;
    ErrorMessage : String;
  end;
  TQueueRequest = TThreadedQueue<TThreadDataRequest>;
  TQueueAnswer = TThreadedQueue<TThreadDataAnswer>;

  TExcampleThread = class(TThread)
  protected
    FQueueRequest: TQueueRequest;
    FQueueAnswer: TQueueAnswer;
    procedure Execute; override;
  public
    constructor Create(AQueueRequest : TQueueRequest; AQueueAnswer : TQueueAnswer); reintroduce;
  end;

  TFormMain = class(TForm)
    Memo: TMemo;
    Layout1: TLayout;
    Label1: TLabel;
    Label2: TLabel;
    ButtonSum: TButton;
    ButtonMult: TButton;
    ButtonDiv: TButton;
    EditA: TEdit;
    EditB: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ButtonClick(Sender: TObject);
  private
    { Private declarations }
    FQueueRequest: TQueueRequest; // Очередь запросов
    FQueueAnswer : TQueueAnswer;  // Очередь ответов
    FExcampleThread : TExcampleThread;
    procedure OnApplicationIdle(Sender : TObject; var Done: Boolean);
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

{$R *.fmx}

procedure TFormMain.FormCreate(Sender: TObject);
begin
  FQueueRequest:=TQueueRequest.Create(10, 1000, 10);
  FQueueAnswer:=TQueueAnswer.Create(10, 1000, 10);
  Application.OnIdle:=OnApplicationIdle;
  FExcampleThread:=TExcampleThread.Create(FQueueRequest, FQueueAnswer);
end;

procedure TFormMain.FormDestroy(Sender: TObject);
begin
  if Assigned(FExcampleThread) then
  begin
    FExcampleThread.Terminate;
    FExcampleThread.WaitFor;
    FExcampleThread.Free;
  end;
  if Assigned(FQueueRequest) then
    FQueueRequest.Free;
  if Assigned(FQueueAnswer) then
    FQueueAnswer.Free;
end;

procedure TFormMain.OnApplicationIdle(Sender : TObject; var Done: Boolean);
Var AThreadDataAnswer : TThreadDataAnswer;
    S : String;
begin
  if FQueueAnswer.PopItem(AThreadDataAnswer) = TWaitResult.wrSignaled then
  begin
    case AThreadDataAnswer.Operation of
      TThreadOperation.Sum : S:=' + ';
      TThreadOperation.Mult : S:=' * ';
      TThreadOperation.Divinity : S:=' / ';
    end;
    S:=AThreadDataAnswer.A.ToString + S + AThreadDataAnswer.B.ToString + ' = ';
    if Not AThreadDataAnswer.ErrorMessage.IsEmpty then
      S:=S + AThreadDataAnswer.ErrorMessage
    else
      S:=S + AThreadDataAnswer.X.ToString;
    Memo.Lines.Add(S);
  end;
end;

constructor TExcampleThread.Create(AQueueRequest : TQueueRequest; AQueueAnswer : TQueueAnswer);
begin
  FQueueRequest:=AQueueRequest;
  FQueueAnswer:=AQueueAnswer;
  Inherited Create(False);
end;

procedure TExcampleThread.Execute;
Var AThreadDataRequest : TThreadDataRequest;
    AThreadDataAnswer : TThreadDataAnswer;
begin
  while Not Terminated do
  begin
    if FQueueRequest.PopItem(AThreadDataRequest) = TWaitResult.wrSignaled then
    begin
      AThreadDataAnswer.Operation:=AThreadDataRequest.Operation;
      AThreadDataAnswer.ErrorMessage:='';
      AThreadDataAnswer.A:=AThreadDataRequest.A;
      AThreadDataAnswer.B:=AThreadDataRequest.A;
      AThreadDataAnswer.X:=0;
      try
        case AThreadDataRequest.Operation of
          TThreadOperation.Sum : AThreadDataAnswer.X:=AThreadDataRequest.A + AThreadDataRequest.B;
          TThreadOperation.Mult : AThreadDataAnswer.X:=AThreadDataRequest.A * AThreadDataRequest.B;
          TThreadOperation.Divinity : AThreadDataAnswer.X:=AThreadDataRequest.A / AThreadDataRequest.B;
        end;
      except
        on E : Exception do
          AThreadDataAnswer.ErrorMessage:=E.Message;
      end;
      FQueueAnswer.PushItem(AThreadDataAnswer);
    end;
    TThread.Sleep(10);
  end;
end;

procedure TFormMain.ButtonClick(Sender: TObject);
Var AThreadDataRequest : TThreadDataRequest;
begin
  AThreadDataRequest.A:=EditA.Text.ToDouble;
  AThreadDataRequest.B:=EditB.Text.ToDouble;
  Case TButton(Sender).Tag of
    1 : AThreadDataRequest.Operation:=TThreadOperation.Sum;
    2 : AThreadDataRequest.Operation:=TThreadOperation.Mult;
    3 : AThreadDataRequest.Operation:=TThreadOperation.Divinity;
  End;
  FQueueRequest.PushItem(AThreadDataRequest);
end;

end.

 

test116 TThreadedQueue.zip

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...