TTMSFMXCircularGauge doesnot work in MultiThread?

DELPHI XE5 up2, WIN 7 32BIT, Problem both in VCL and FMX.

Thanks!!!

--------------------------------------------------------------------
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.TMSBaseControl, FMX.TMSGauge;

type
  TForm1 = class(TForm)
    Button1: TButton;
    TMSFMXCircularGauge1: TTMSFMXCircularGauge;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    myThread: TThread;
    procedure XXX;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.XXX;
BEGIN
  TMSFMXCircularGauge1.Value := Random(100);
END;

procedure TForm1.Button1Click(Sender: TObject);
begin
  myThread := TThread.CreateAnonymousThread(XXX);
  myThread.FreeOnTerminate := False;
  myThread.Start;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  v: double;
begin
  v := Random(100);
  TMSFMXCircularGauge1.Value := v;
end;

end.

Whatever I use such as

TMSFMXCircularGauge1.Repaint; Update; InvalidateRect; 
But it never work! Please help me!

a Repaint, Invalidate, and all other paint actions are not thread-safe. You need to call synchronize to update the main thread:


procedure TForm1163.Button1Click(Sender: TObject);
begin
  myThread := TThread.CreateAnonymousThread(XXX);
  myThread.FreeOnTerminate := False;
  myThread.Start;
end;

procedure TForm1163.XXX;
begin
  myThread.Synchronize(nil, XXXSync);
end;

procedure TForm1163.XXXSync;
begin
  TMSFMXCircularGauge1.Value := Random(100);
end;

Thanks, you are so kind and it works.

But I have another question:

----------------------------------------------------------------------
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.TMSBaseControl, FMX.TMSGauge;

type
  TForm1 = class(TForm)
    Button1: TButton;
    TMSFMXCircularGauge1: TTMSFMXCircularGauge;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
    m_close: Boolean;
    myThread: TThread;
    procedure XXX;
    procedure XXXSync;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.XXX;
begin
  myThread.Synchronize(nil, XXXSync);
end;

procedure TForm1.XXXSync;
begin
  repeat
  begin
    // in fact, here we do some io operations, then give the value to Gauge1
    // but how I can stop the application by close button of Mac ?
    TMSFMXCircularGauge1.Value := Random(100);
  end
  until (m_close);
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  m_close := True;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  m_close := False;
  myThread := TThread.CreateAnonymousThread(XXX);
  myThread.FreeOnTerminate := False;
  myThread.Start;
end;

end.

When I don't use Synchronize, I can easily close the application.


So, I want the data can be synchronized and shown in the main form, but the close button don't synchronize with the main form(in fact, I use thread just want that the user can easily close the application).

Only the final result needs to be set as value of the circulargauge. The result can be calculated inside the XXX procedure, the visual update needs to be synchronized. You need to replace FGaugeValue := Random() with your io operations code. You also have a memory leak because you do not free the thread and you set FreeOnTerminate to false. A better approach would be to check if the thread is terminated, and terminate the thread in the formclose.


unit Unit1163;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.TMSBaseControl, FMX.TMSGauge;

type
  TForm1163 = class(TForm)
    TMSFMXCircularGauge1: TTMSFMXCircularGauge;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    FGaugeValue: Single;
  public
    { Public declarations }
    myThread: TThread;
    procedure XXX;
    procedure XXXSync;
  end;

var
  Form1163: TForm1163;

implementation

{$R *.fmx}

procedure TForm1163.XXX;
begin
  while not myThread.CheckTerminated do
  begin
    // in fact, here we do some io operations, then give the value to Gauge1
    // but how I can stop the application by close button of Mac ?
    FGaugeValue := Random(100);
    myThread.Synchronize(nil, XXXSync);
  end;
end;

procedure TForm1163.XXXSync;
begin
  TMSFMXCircularGauge1.Value := FGaugeValue;
end;

procedure TForm1163.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  myThread.Terminate;
end;

procedure TForm1163.FormCreate(Sender: TObject);
begin
  myThread := TThread.CreateAnonymousThread(XXX);
  myThread.FreeOnTerminate := True;
  myThread.Start;
end;

end.

Kind Regards, 
Pieter

Pieter Scheldeman2014-04-04 07:58:20

Hi i've same problem on a thread like this

While Not Terminated Do Begin

Try Try
    //Synchronize(nil,
    Queue(nil,  // Queue è asincrono e 'refresha' meglio di Synchronize
      Procedure
      Begin
         circulargaugeLTOUsc.Value:=....
         circulargaugeLTOUsc.DisplayText := ...
         circulargaugeTDS.Value:=...
         circulargaugeTDS.DisplayText := ...
     end;

Go fine until i do :
MonThread.Terminate();
MonThread.WaitFor();
FreeAndnil(MonThread);

but if i restart thread
MonThread:=TMonThread.Create(False);

animation freeze ... but circulargaugeTDS.DisplayText go fine show changed text. Thare is any way to refresh/repaint animation ?

Did you try the code snippet I posted?

i this same code . entire code here

formcreate

  If Not Assigned(MonThread) Then Begin
    MonThread:=TMonThread.Create(False);
    {$IFDEF WIN32}
      MonThread.Priority:=tpTimeCritical;
    {$ENDIF}
  End;

// -----------------------------------------------------------------------------
procedure TMonThread.Execute();
begin
While Not Terminated Do Begin

Try Try
    //Synchronize(nil,
    Queue(nil,  // Queue è asincrono e 'refresha' meglio di Synchronize
      Procedure
      Begin
                  cgLTOUsc.Value:=StrToIntDef(frmTDPComPort.MonValues.Strings[lstLTO_USC_CORR],0);
                  // visualizza il valore in dettaglio
                  cgLTOUsc.DisplayText:=frmTDPComPort.MonValues.Strings[lstLTO_USC_CORR];

                  cgTDS.Value:=StrToIntDef(frmTDPComPort.MonValues.Strings[lstTDS_CORR],0);
                  // visualizza il valore in dettaglio
                  cgTDS.DisplayText:=frmTDPComPort.MonValues.Strings[lstTDS_CORR];
      End);
  End;
Except
  On E:Exception Do Begin
      If Assigned(MonThread)Then Begin
        MonThread.Terminate();
        MonThread.WaitFor();
        FreeAndnil(MonThread);
      End;

  End;
End;
Finally
  // fa qualcosa
End;

Sleep(100); // per evitare l'uso intensivo di CPU

End;
end;

// -----------------------------------------------------------------------------
Function TfrmTDPMon.CloseForm
Begin

If Assigned(MonThread)Then Begin
MonThread.Terminate();
MonThread.WaitFor();
FreeAndnil(MonThread);
End;

NOTE : circular objects not destroyed on CloseForm, form is always alive !

The code snippet you posted is NOT the same code as the code snippet in my post. Please try the code snippet in my post. I have retested this here and there are no issues with the code. If you are using another code snippet for threading and you want to access the circular gauge from within the thread, but you get issues when destroying, you are doing something wrong. Please revise the code.

Nothing i tried to call : Synchronize(nil, SyncCG); in main thread
and the procedure

Procedure TfrmTDPMon.SyncCG();
Begin
cgLTOUsc.Value:=StrToIntDef(frmTDPComPort.MonValues.Strings[lstLTO_USC_CORR],0);
// visualizza il valore in dettaglio
cgLTOUsc.DisplayText:=frmTDPComPort.MonValues.Strings[lstLTO_USC_CORR];
cgTDS.Value:=StrToIntDef(frmTDPComPort.MonValues.Strings[lstTDS_CORR],0);
// visualizza il valore in dettaglio
cgTDS.DisplayText:=frmTDPComPort.MonValues.Strings[lstTDS_CORR];
End;