Thread safety and IBXComponents variable
Hello
Is it possible to write thread safe reports using frxIBXComponents ? There is (Delphi 5 Source) a global variable:
var
IBXComponents: TfrxIBXComponents;
which seems to be a problem in multithreaded enviroment.
I want to prepare and create two or more reports in separate threads in the same time.
b
Is it possible to write thread safe reports using frxIBXComponents ? There is (Delphi 5 Source) a global variable:
var
IBXComponents: TfrxIBXComponents;
which seems to be a problem in multithreaded enviroment.
I want to prepare and create two or more reports in separate threads in the same time.
b
Comments
I've asked about IBX : ) not ADO. I don't know if there is same solution like global IBXComponents variable ? maybe ADOComponents variable ? Can You check this ?
b
Mick
Database not assigned
invalid request handle
Error reading data from the connection
invalid transaction handle (expecting explicit transaction start)
unknown ISC error 0
Transaction not assigned
and many others. Those problem occurs "sometimes" when two threads prepare two reports in the same time.
Is it so hard to believe that global variable maybe a problem in simultaneously preparing reports from threads ?
But OK - i will try to prepare a test case.
P.S. in GPi example there are lot of other errors : ) but most of all are result of access to components from thread without synchronization ...
{...}
procedure TPrepareThread.Done;
begin
FLB.Caption := 'Done';
FBtn.Enabled := True;
end;
procedure TPrepareThread.Ended;
begin
FLB.Caption := 'Not Started';
FBtn.Enabled := False;
end;
{...}
It's bad idea - but I know that is only example of other issue : )
b
P.S. Sorry for my comment about synchronization : ) I didn't notice Synchronization method before : ) However i get a lot of error when running this example.
b
- create new datamodule which contains:
- frxreport
- frxADOComponents
- ADOConnection
- frxADOComponents.DefaultDatabase point to ADOConnection, ADOConnection points to demo.mdb
- in the 1.fr3 report i've removed the ADODatabase, and change query database to ADOConnection
- each thread load and prepare the same report - based on 1.fr3 (saved as 2.fr3 and 3.fr3)
- thread instead of createing frxreport - creates datamodule
And now, when I click button 1, button 2, button 3 It works like before, but when I put button 4 with code:
FRepThread1 := TPrepareThread.Create(AppPath + '1.fr3', Button4, Label1);
FRepThread2 := TPrepareThread.Create(AppPath + '2.fr3', Button5, Label2);
FRepThread3 := TPrepareThread.Create(AppPath + '3.fr3', Button6, Label3);
there are many different errors like "OLE Error", "Operation not allowed when ..."
b
b
After removing frxADOComponent from Datamodule, and putting back ADOConnection into 1.fr3, all works fine (even fire all three report in the same time in timer).
So - my final question is:
It is possible to prepare and create report's in thread using frxXXXComponents (frxIBXComponents, frxADOComponents) ?
b
I gues that this is not the solution for primary problem ; )
b
I will prepare a test case later.
But generally I gues that Your reply will be: frxXXXComponents are not intended to be used in threads in such way : )
b
Yes I do, but - is there such variable:
var
ADOComponents: TfrxADOComponents;
? I don't have a source, but in frxIBXComponent there is such variable:
var
IBXComponents: TfrxIBXComponents;
and imho that is the problem.
I've attached Your reworked example.
b
var
ADOComponents: TfrxADOComponents;
over there.
By the way - how did it happen that you have frxIBXComponent.pas and do not have frxADOComponents.pas ?
Mick
You right, there is frxADOComponents. When i clicked on frxADOComponents Delphi didn't bring me into it - I don't know why.
So if this is global variable, declared at unit level - it can be a problem, am i right ?
b
Finally ! : )
I don't want to use TfrxIBXDatabase in each report, because of many reasons:
- user doesn't have to know nothing about database location
- user doesn't have to know about user / pass
- it is easier for user to have one component (tfrxibquery) than two components (tfrxibxdatabase + tfrxibquery)
- changing database location, can be transparent for reports
... etc. etc. etc.
There is on more reason. I've many users, with many reports, without TfrxIBXDatabase component : )
Solution with TfrxDBDataset, uhm it not sound good for me.
Tommorow we'll try my own solution - we will use frxIBXComponent only for desing report, but when we will need execute them in thread we can do someting like this (after loading report from database):
frxReport1.GetDataSetList(myList);
For item in myList do
(frxReport1.GetDataset(item).Component as TIBQuery).Database := myDatabase;
This is only a sketch - we will test it tommorow.
When reports are executed from main thread I use TfrxIBXComponent in main thread - then there is only one thread = main thread. When reports are executed in thread's (child threads of service application) I can not use same database for all of them - its forbidden (I believe You know why).
I want to centralize all logic in the same unit (TDataModule). An application have one instance of this datamodule. This object is responsible for loading from DB / preparing / executing / printing / exporting reports. In service, all child thread create have own instance of this datamodule. Those threads can load from DB / prepare / execute report using the same code. I was working great, until we program our service to execute more than one report at exactly same time. That is how I found this problem.
However - now I'm sure that this is not my mistake - thats FR desing issue. So thanks for help ! As I mention above, tomorrow I will try solution with GetDataSetList().
I tried to follow your idea about this application. I'm not sure if I understood everything well (the application run as an operating system service or end user decides when he wants it to run). But when you face such a problem - have you thought about making two twin applications? One main to do things as before, and second just to print reports in threads? Is this possible in your case to change the logic of data manipulation in that way?
Mick
Yes I did. But it is easier for me and for users to have one concept of creating reports (without need to define connection). User can use such report in two ways - print it from application, or schedule that in a service.
Of course there is a lot of {$IFDEF SRV}, so that souldn't be problem do add another one {$IFDEF} - in order to use frxIBXComponents only in application. If idea with GetDataSetList will work - I will be satisfying solution for me.
Rather not. As i wrote before this mechanism is about two years old...
Cool ( ;
b