Page 1 of 2

Connection and Threads ;)

Posted: Thu 31 Mar 2005 15:25
by Guest
Hello,

I have a Datamodule.
On this DM, there is several MyQuery comps and one MyConnection comp.
The MyConnection Pooling option is set to true.

I open all the myquery comps into a thread.
For each thread, I create a new MyConnection. The created MyConnection has the same options / properties (except his name) as the one defined in the DM.

Code: Select all

  fMyConnection:=TMyConnection.Create(nil) ;
  with fMyConnection do
  Begin

    loginprompt:=false;
    pooling:= TCustomMyDataSet(FDataset).Connection.pooling;
    PoolingOptions  := TCustomMyDataSet(FDataset).Connection.PoolingOptions;

    Server  := TCustomMyDataSet(FDataset).Connection.Server;
    Username := TCustomMyDataSet(FDataset).Connection.Username;
    Password  := TCustomMyDataSet(FDataset).Connection.Password;
    Database := TCustomMyDataSet(FDataset).Connection.Database;
    IsolationLevel  := TCustomMyDataSet(FDataset).Connection.IsolationLevel;
    Port := TCustomMyDataSet(FDataset).Connection.Port;
    ConnectionTimeout := TCustomMyDataSet(FDataset).Connection.ConnectionTimeout;

    options := TCustomMyDataSet(FDataset).Connection.options;

    fMyDefaultConnection :=TCustomMyDataSet(FDataset).Connection;

  End;
TCustomMyDataSet(FDataset).Connection:=fMyConnection;

Data are correctly loaded.

But when I want to disconnect from the MySQL server, my queries opened via m threads are not closed ! They have they proper Myconnection object !

So I'm no more able to close them. I tried to replace the thread MyConnection object with the DM MyConnection object. This only close my query.

Code: Select all

TCustomMyDataSet(FDataset).Connection:=fMyDefaultConnection;
Is there a solution ???

Thanks

Posted: Fri 01 Apr 2005 08:19
by Ikar
It is the significant specific of ConnectionPooling work - after finishing working with TMyConnection a connection to the server is not broken but left in the pool till a next query to the server appears.

Posted: Fri 01 Apr 2005 13:14
by Guest
I have perhaps been missunderstood... or maybe, I was not clear enougth with my problem.

Some of my queries / tables are openend into a thread.I create a thread because of the high records I need to fetch. Alos, by doing this, my software will not "hang" while records are fetched.

Some of my queries (lockup tables) are openend in the main thread. As records are quickly loaded, there is no need to put them into a thread.

After opening Thraded and non threaded tables, queries, making things I wanted to do, I want to :

Datamodule.MyConnection.Disconnect;

In a perfect world, this should :
- close all opened queries and tables
- close the mysql connection

In my case, only the tables and queries linked to the Datamodule .MyConnection are closing.

Queries and tables openened into my thread (where I have created a separate Myconnection object for each thread) don't...

I my thread, when I create my Myconnection Object, I recover all the properties from my "Datamodule" Myconnection Object. Both have the pooling property set to true.

Please notice that I don't create any now MyQuery / MyTable object. I just do that :

PDataset := FDataset;

Where PDataset is a Private Dataset from my thread, and FDataset, is the Dataset from the Datamodule form I choose to open in the thread.

Posted: Fri 01 Apr 2005 14:59
by Ikar
If you use DataModule.FDataset why you need to create a new instance of TMyConnection? You can use already existent DataModule.FConnection!

Posted: Mon 04 Apr 2005 07:47
by Guest
Humm, If don't create a MyConnection object, i'm getting an "Command Out of synch" with copmpress option set to false.... and a packet out of order with the compress option set to true.

Here is my unit source code :

Code: Select all

unit MyDataSetThread;

interface

uses
  Classes, DB,cxGridDBTableView,DBAccess, MyAccess,dialogs;

type

  // ---------------------------------------------------------------------------
  // Class that encapsulate a TDataset into a thread
  //   Remark :   TDataSet must be correctly set.
  // ---------------------------------------------------------------------------
  TDataSetThread = class(TThread)
  protected
    FcxGridDBTableView : TcxGridDBTableView ; // cxGridView auquel est branché le DataSet
    fMyConnection,fMyDefaultConnection : TMyConnection;
    fFieldsCaptions: TStrings;
    FDataSet : TDataSet;       // DataSet encapsulé
    FDataSource : TDataSource; // DataSource auquel est branché le DataSet
    FMessages : TStrings;      // Pour stocker les messages éventuels
    FMessage : string;         // Message à écrire
    procedure initialisation(pDataSet : TDataSet; pDataSource : TDataSource; pcxGridDBTableView : TcxGridDBTableView;pFieldsCaptions,pMessages : TStrings);
    procedure WriteMessage;
    procedure ProcessEnd;
    procedure Execute; override;
    procedure ThreadOnTerminate(Sender: TObject);
  public
    // Creation with a Dataset only
    constructor Create(pDataSet : TDataSet;  pFieldsCaptions : TStrings = nil;pMessages : TStrings = nil); overload;
    // Creation with a TDataSource : in this case, his DataSet is  encapsulated
    constructor Create(pDataSource : TDataSource;  pFieldsCaptions : TStrings = nil;pMessages : TStrings = nil); overload;
    // Creation with a TcxGridDBTableView : in this case, his DataSet is  encapsulated
    constructor Create(pcxGridDBTableView : TcxGridDBTableView;  pFieldsCaptions : TStrings = nil;pMessages : TStrings = nil); overload;
  end;

  TDataSetExportThread = class(TDataSetThread)
  private
    FFileName : string;
  protected
    procedure Execute; override;
  public
    constructor Create(pDataSet : TDataSet; pFileName : string; pFieldsCaptions : TStrings = nil;pMessages : TStrings = nil); overload;
    constructor Create(pDataSource : TDataSource; pFileName : string; pFieldsCaptions : TStrings = nil;pMessages : TStrings = nil); overload;
    property FileName : string read FFileName write FFileName;
  end;

implementation

uses
  SysUtils;

{ TDataSetThread }

procedure TDataSetThread.initialisation(pDataSet: TDataSet; pDataSource: TDataSource;pcxGridDBTableView : TcxGridDBTableView; pFieldsCaptions,pMessages: TStrings);
begin
  FDataSource := pDataSource;
  FDataSet := pDataSet;
  FcxGridDBTableView:= pcxGridDBTableView;



  if assigned(FDataSource) then
    FDataSource.DataSet := nil;      // Unlink the Dataset from the Datasource

  fMyConnection:=TMyConnection.Create(nil) ;
  with fMyConnection do
  Begin

    loginprompt:=false;
    pooling:= TCustomMyDataSet(FDataset).Connection.pooling;

    PoolingOptions  := TCustomMyDataSet(FDataset).Connection.PoolingOptions;

    Server  := TCustomMyDataSet(FDataset).Connection.Server;
    Username := TCustomMyDataSet(FDataset).Connection.Username;
    Password  := TCustomMyDataSet(FDataset).Connection.Password;
    Database := TCustomMyDataSet(FDataset).Connection.Database;
    IsolationLevel  := TCustomMyDataSet(FDataset).Connection.IsolationLevel;
    Port := TCustomMyDataSet(FDataset).Connection.Port;
    ConnectionTimeout := TCustomMyDataSet(FDataset).Connection.ConnectionTimeout;

    options := TCustomMyDataSet(FDataset).Connection.options;


    fMyDefaultConnection :=TCustomMyDataSet(FDataset).Connection;



    connect;
  End;
  TCustomMyDataSet(FDataset).Connection:=fMyConnection;
  FMessages := pMessages;
  FreeOnTerminate := True;           // Thread destructs himsefl when finished
  FFieldsCaptions:=pFieldsCaptions;
  OnTerminate := ThreadOnTerminate;

end;

procedure TDatasetThread.ThreadOnTerminate(Sender: TObject);
Begin
    sender:=nil;
End;

constructor TDataSetThread.Create(pcxGridDBTableView : TcxGridDBTableView;pFieldsCaptions: TStrings ; pMessages: TStrings);
begin
  inherited Create(true);          // thread is halted when created
  initialisation(pcxGridDBTableView.DataController.DataSet, pcxGridDBTableView.DataController.DataSource,pcxGridDBTableView, pFieldsCaptions,pMessages);
  resume;
end;

constructor TDataSetThread.Create(pDataSource: TDataSource; pFieldsCaptions: TStrings ;pMessages: TStrings);
begin
  inherited Create(true);          // thread is halted when created
  initialisation(pDataSource.DataSet, pDataSource,nil, pFieldsCaptions,pMessages);
  resume;
end;


constructor TDataSetThread.Create(pDataSet: TDataSet; pFieldsCaptions,pMessages: TStrings);
begin
  inherited Create(true);          // thread is halted when created
  initialisation(pDataSet, nil,nil, pFieldsCaptions,pMessages);
  resume;
end;

procedure TDataSetThread.WriteMessage;
begin
  if assigned(FMessages) then
    FMessages.Add(FMessage);
end;

procedure TDataSetThread.Execute;
begin
  inherited;
  try
    FDataSet.Open;
  except
    on e: Exception do
    begin
      FMessage := e.Message;
      // We write the message in FMessages
      // as FMessages could be shared with other threads (a
      //  TMemo for example), we Synchronize   it
      Synchronize(WriteMessage);
    end;
  end;
  Synchronize(ProcessEnd);
end;

procedure TDataSetThread.ProcessEnd;
var i,j : integer;
begin
  //disable controls
  if assigned(FFieldsCaptions) then
  Begin
    FDataSet.disablecontrols;
    FDataSet.FieldDefs.Clear;
    FDataSet.FieldDefs.Updated:=false;
    FDataSet.FieldDefs.Update;
  End;

  // we link the Dataset to the datasource
  if assigned(FDataSource) then
    FDataSource.DataSet := FDataSet;

  if assigned(FcxGridDBTableView) then
  Begin
    FcxGridDBTableView.BeginUpdate;
    FcxGridDBTableView.ClearItems ;
    FcxGridDBTableView.DataController.CreateAllItems ;
    FcxGridDBTableView.EndUpdate;
  End;
  //enable controls
  FDataSet.enablecontrols;

end;

end.

Posted: Mon 04 Apr 2005 07:47
by Guest
In the code above, remove the obsolete "TDataSetExportThread = class(TDataSetThread)"

Posted: Mon 04 Apr 2005 10:52
by Ikar
> i'm getting an "Command Out of synch" with copmpress option set to false....
> and a packet out of order with the compress option set to true.


Most likely you use the old version of MyDAC. Try to update MyDAC to version 3.50.0.18 and repeat the test.

Posted: Mon 04 Apr 2005 13:01
by Guest
This is the latest one !, in the about box, I can see version 3.50.0.18 !

I guess you can easyly reproduce my Error :

Just create a new poject
Add a datamodule

On this Datamodule put :
1 MyConnection comp
2 MyQuery com


Associate datasources to the Dataset.

Now on the main form

Put either (if you have it) 2 cxGrids or 2 DBGRid
Link the grids to the corresponding Datasources.

Add the MyThreadedDAtaset unit to the main form

create a button and in the Onclick event write this :

Code: Select all

  try

    DM.myconnection.LoginPrompt:=true;
    DM.myconnection.connect;
   //Let's create the new thread....
    TDataSetThread.Create(fmMain.cxGridDBTableView1);
    TDataSetThread.Create(fmMain.cxGridDBTableView2);
  except
    on e: exception do
    begin
       //I'm doing this because when you use a custom dialog box, 
      //Clicking the 'Cancel' button wil raise an silent exception
       raise Exception.Create(e.message) ;
    end;
TDataSetThread.Create

Posted: Tue 05 Apr 2005 09:17
by Ikar
Did I understand you correctly that you use a single connection simultaneously from the different threads? In this case any errors are possible - starting from "Command out of sync" to "Access Violation". If the reason of the problem is another please send us small complete sample to mydac support address (mydac*crlab*com).

Posted: Tue 05 Apr 2005 10:29
by Guest
I'm opening some queries on my main thread.
This queries are linked to a single Connection.

If I open queries into a thread, and use my single connection, i'm getting errors (commands out of synch, net packet out of orders and so on).

To avoid this errors, in the thread, before opening the query, I create a new connection. When doing that, it is working But....

When I close my software, I'm doing a
Datamodule.Myconnection.close;

Only the queries opened in the main thread are closed.
All queries opened in a thread are not closed.

I understand that Queries opened in a thread are linked to another connection component. How can I change this property (without closing the query and losing my fetched records) and tells the Query to use the single connection ?

I'm I doing something so meaningless ???

Posted: Tue 05 Apr 2005 22:38
by GEswin
I think that what this user want to do is just load data in background so that appz doesn't look freezed when loading huge tables. I have the same problem too , i would like to load big amount of data without having the application freezed for a time. Just doing this by a thread or at some point having an application.processmessages called from time to time. I didn't find a solution right now.

Posted: Wed 06 Apr 2005 08:21
by Guest
Yes :P , that's it !

Posted: Wed 06 Apr 2005 12:51
by Ikar
Using threads in this case is quite reasonable. The only you have to take care is not to apply for this MyQuery and MyConnection till completing fetch. Time spent on opening an additional connection in comparison with fetch time will be insignificant.

Posted: Thu 07 Apr 2005 03:30
by kenny
Hi,
I'm actually also finding a way to open the huge table in background silently to avoid freeze screen...

I'd tried to apply the way in this topic but also get the same errors on it.

Hopefully it's a way to achieve it....

Posted: Thu 07 Apr 2005 07:03
by Guest
Hi all,

Ikar, could you provide us a way for doing this ?, opening dataset in the background interrest a lot of people.

Thank you for your precious help