• 0
Akad

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

Вопросы

С недавних пор (студия 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 ответов на этот вопрос

  • 0

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

control := TEdit.Create(nil);

control.Parent := parent;

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

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

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