I've created a new DUnit Test Project and I am trying to setup multithreaded apartment at its start. The problem is that on one computer apartment type is changed.
program COMApartment;
{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
Winapi.ActiveX,
TestuApartmentInfo in 'TestuApartmentInfo.pas',
DUnitTestRunner;
{R *.RES}
begin
CoUninitialize;
CoInitializeEx(nil, COINIT_MULTITHREADED); // Result is S_OK
Log(GetCurrentApartmentType); //APTTYPE_MTA on both computers.
DUnitTestRunner.RunRegisteredTests;
end.
Now, when I run this simple test:
unit TestuApartmentInfo;
interface
uses
TestFramework, Winapi.Windows, uApartmentInfo;
type
TestIComThreadingInfo = class(TTestCase)
published
procedure ApartmentType;
end;
implementation
uses
Dialogs, System.SysUtils;
procedure TestIComThreadingInfo.ApartmentType;
begin
//This gives APTTYPE_MTA on my dev computer (Windows 7) and APTTYPE_MAINSTA or APTTYPE_STA on virtual machine (Windows 2007 Server).
Log(GetCurrentApartmentType);
end;
initialization
RegisterTest(TestIComThreadingInfo.Suite);
end.
I don't understand different behaviour on different computers. Is this due to different operating systems? In my test I could spawn another thread and specify apartment model for it and it would work, but my curious nature wants to know why different results in above case.
GetCurrentApartmentType has been implemented as in this article and it works properly. This is a sample application to illustrate problem which I have with some COM objects that need to run in multithreaded apartment model.
Solution to my problem is described in this post. My mistake was calling CoUninitialize/CoInitialize after some other code already called it.
CoInitialize should be called before any other code.
Related
I have 10 threads working together. After starting the threads, 15 seconds later all threads exit before the job done, and only one thread remains.
My code:
procedure TForm1.Button2Click(Sender: TObject);
begin
AA;
BB;
CC;
DD;
EE;
FF;
GG;
HH;
II;
JJ;
end;
procedure TForm1.AA; //same procedure for BB,CC,DD,EE.FF,JJ,HH,II,JJ
begin
lHTTP := TIdHTTP.Create(nil);
TTask.Create(Procedure
try
//HTTP Opertations
finally
end;
end).Start;
end;
Note, i can't Free the HTTP component because if i did i get an AV and I don't know how to debug it, where to correctly free it in the code? However without freeing it the code works well but the threads exit. It might be the problem as Mr Dodge said.
Based on how I see you're creating the TIdHTTP component, it's simply wrong. You shouldn't create an object outside of the thread, then use it from inside the thread. That's not thread-safe. You should create it in the same thread as where it's being used. This is why you're unable to free it as well, so you actually have two problems to fix here at the same time.
I also realized that your lHTTP variable is not in the scope of your code, so I'm going to assume that you have it declared in some global (or otherwise shared) location. Each thread needs its own variable for its own instance.
So your code should look a little more like this:
procedure TForm1.AA; //same procedure for BB,CC,DD,EE.FF,JJ,HH,II,JJ
begin
TTask.Create(Procedure
var
lHTTP: TIdHTTP;
begin
lHTTP := TIdHTTP.Create(nil);
try
//HTTP Opertations
finally
lHTTP.Free;
end;
end).Start;
end;
Other components (such as TADOConnection) would even completely fail and crash for attempting such a thing (since such components utilize COM). Luckily, TIdHTTP does not use COM, but the design is still flawed for the same reason.
Now, when you say that you debugged it, I'm guessing you mean you debugged the code in the actual thread, but the breakpoint jumped to another place in your code before it reached the end of this? That is to be expected when using the debugger in threads. You can't just step into a thread and expect each sequential breakpoint to be in the same thread - I mean, if you have more than one breakpoint in different threads, your debugger is very likely to jump from one to another - because, again, they are multiple threads. I suggest creating some sort of work log, and each thread reports its status and position.
It is literally just like an alternate universe. Multiple different similar threads doing slightly different things than each other. The Delphi Debugger is simply the Time Lord who can see into all the alternate universes.
I have an out-of-process COM server originally developed in Delphi 7, then D2006, recently recompiled under XE3. Under the XE3 version, requests are processed one at a time. If one is in progress when another comes in, ObjectCount goes up, but the second isn't processed until the first finishes. The D7/D2006 versions processed the requests concurrently.
The server maintains a database. Most requests are processed very quickly, but a few take a minute or more. There is an form with a Memo that displays an activity log, that's the whole UI. Not sure what other information would be helpful here.
Any ideas why the XE3 version would behave differently? Or, in general, why the second server object might wait for the first to finish?
Here's an example. I installed XE5, created a VCL app, and added a COM object with one method, and created a test client. It processed one request at a time. I compiled the same project in D2006 (just had to remove the unit prefixes) and it processed them concurrently.
What am I doing wrong?
unit Unit2;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Windows, ActiveX, Classes, ComObj, Project1_TLB, StdVcl;
type
TTest = class(TTypedComObject, ITest)
protected
function Method1(N: Integer): HResult; stdcall;
end;
implementation
uses ComServ;
function TTest.Method1(N: Integer): HResult;
var I: Integer;
begin
for I := 1 to N do
Sleep( 1000);
end;
initialization
TTypedComObjectFactory.Create(ComServer, TTest, Class_Test,
ciMultiInstance, tmFree);
end.
Double check the threading model of each version of the COM object. It sounds like the XE3 version is Apartment-threaded (where COM serializes requests) while the earlier version is Multi/Free-threaded instead (where COM does not serialize requests).
I traced through the simple Delphi XE5 server above. Application.Initialize -> ComServ.InitComServer -> ComObj.InitComObj -> CoInitializeEx( nil, COINIT_MULTITHREADED), which returns RPC_E_CHANGED_MODE. The server processes one request at a time.
On tracing through the same project compiled under D2006, the CoInitializeEx call returns S_OK, and the server processes requests concurrently.
I added a call to CoUninitialize in the project source before the call to Application.Initialize. With that, the XE5 version processes requests concurrently. This worked for the production server that prompted the question, too.
In Delphi 2009, synchronize'd functions that worked fine in Delphi 7 execute with sublime slowness until you wiggle the mouse over the open form. Wiggling the mouse causes the program to go into some mouse idle state which does CheckSynchronize(). CheckSynchronize() appears to be called less frequently in Delphi 2009 than in Delphi 7, but we can't figure out why or where.
Placing this code:
procedure TMyForm.FormCreate(Sender : TObject)
Classes.WakeMainThread := WakeMainThread;
end;
procedure TMyForm.WakeMainThread(Sender: TObject);
begin
SendMessage(Application.MainForm.Handle, WM_NULL, 0, 0);
end;
Speeds things along at the normal rate. But I get: EAccessViolation in module rtl120.bpl when the thread dies if it's used in a modal app (works fine for a simple dialog utility). I'm guessing 'Classes.WakeMainThread()' is being called just as much as it is in Delphi 7, but sending WM_NULL to application.handle isn't accomplishing anything. I guess it's time to 'get steppin'.
The internals of the Synchronize() mechanism have not changed much between D7 and D2009. Sure, there have been new features added (asynchronous queuing, anonymous methods, etc), but the core implementation to run code in the main thread has not changed. What is more likely happening is something else in your main thread code that you have not shown yet is blocking the main thread from processing pending messages and Synchronize() requests correctly.
TApplication.Create is getting called from another DLL, hence it is waking an invalid handle or some nonsense in that callback.
You need to eliminate statically linked DLLs that include [vcl.]controls.pas because TApplication.Create happens in some initialization code in that unit.
Once you do this, synchronize will be restored to it's former glory.
Unfortunately, fixes you make in one version of Delphi might be undone by changes made in another version of Delphi. So if the problem comes back, go back to the drawing board. Step through the initialization code, specifically the initUnits procedure in system.pas. It runs the initialization code and will bump into vcl.controls.pas eventually and you can peer into the UnitInfo record to find out which file this is being called from.
The best way to fix this is to use the delayed with all your external dlls (at least all your Delphi VCL external DLLs).
function didntknowIusedcontrolsbutIdo() : Integer; external 'useful.dll' delayed;
But this only works in Delphi 2010 and up. Good thing you upgraded to XE2 between the time you asked this question and the time you finally find a satisfying answer to it.
The app is written in Delphi XE.
I have two classes, a TBoss and TWorker, which are both based of of TThread.
The TBoss is a single instance thread, which starts up and then will create about 20 TWorker threads.
When the boss creates a instance of TWorker it assigns it a method to call synchronize on, when the Worker has finished with what it's doing it calls this method which allows the Boss to access a record on the Worker.
However I feel this is a problem, calling synchronize appears to be locking up the whole application - blocking the main (ui) thread. Really it should just be synchronizing that worker to the boss thread....
Previously I used messages/packed records to send content between threads which worked well. However doing it this way is much cleaner and nicer.... just very blocking.
Is there a way to call Syncronize in the worker to only wait for the Boss thread?
My code:
type
TWorker = class(TThread)
private
fResult : TResultRecord;
procedure SetOnSendResult(const Value: TNotifyEvent);
....
....
public
property OnSendResult: TNotifyEvent write SetOnSendResult;
property Result : TResultRecord read fResult;
....
end;
...
...
procedure TWorker.SendBossResults;
begin
if (Terminated = False) then
begin
Synchronize(SendResult);
end;
end;
procedure TWorker.SendResult;
begin
if (Terminated = false) and Assigned(FOnSendResult) then
begin
FOnSendResult(Self);
end;
end;
Then in my Boss thread I will do something like this
var
Worker : TWorker;
begin
Worker := TWorker.Create;
Worker.OnTerminate := OnWorkerThreadTerminate;
Worker.OnSendResult := ProcessWorkerResults;
So my boss then has a method called ProcessWorkerResults - this is what gets run on the Synchronize(SendResult); of the worker.
procedure TBoss.ProcessWorkerResults(Sender: TObject);
begin
if terminated = false then
begin
If TWorker(Sender).Result.HasRecord then
begin
fResults.Add(TWorker(Sender).Result.Items);
end;
end;
end;
Synchronize is specifically designed to execute code in the main thread; that's why it seems to lock everything up.
You can use several ways to communicate from the worker threads to the boss thread:
Add a callback to each worker thread,
and assign it from the boss thread
when it's created. It can pass back
whatever as parameters, along with a
thread ID or some other identifier.
Post a message from the worker thread
to the boss thread using
PostThreadMessage. The
disadvantage here is that the boss
thread has to have a window handle
(see Classes.AllocateHWnd in the
Delphi help and David Heffernan's comment below).
Use a good quality third-party
threading library. See
OmniThreadLibrary - it's free,
OS, and extremely well written.
My choice would be the third. Primoz has done all the hard work for you. :)
After your comment, here's something along the lines of my first suggestion. Note that this is untested, since writing the code for a TBoss and TWorker thread + a test app is a little long for the time I have right this minute... It should be enough to give you the gist, I hope.
type
TWorker = class(TThread)
private
fResult : TResultRecord;
fListIndex: Integer;
procedure SetOnSendResult(const Value: TNotifyEvent);
....
....
public
property OnSendResult: TNotifyEvent write SetOnSendResult;
property Result : TResultRecord read fResult;
property ListIndex: Integer read FListIndex write FListIndex;
....
end;
type
TBoss=class(TThread)
private
FWorkerList: TThreadList; // Create in TBoss.Create, free in TBoss.Free
...
end;
procedure TWorker.SendBossResults;
begin
if not Terminated then
SendResult;
end;
procedure TBoss.ProcessWorkerResults(Sender: TObject);
var
i: Integer;
begin
if not terminated then
begin
If TWorker(Sender).Result.HasRecord then
begin
FWorkerList.LockList;
try
i := TWorker(Sender).ListIndex;
// Update the appropriate record in the WorkerList
TResultRecord(FWorkerList[i]).Whatever...
finally
FWorkerList.UnlockList;
end;
end;
end;
end;
You could use a thread safe queue. In DelphiXE there is the TThreadedQueue. If you don't have DXE, try OmniThreadLibray - this library is very good for all threading issues.
As I mentioned new options in Delphi 2009 and higher, here is a link to an example for Producer / Consumer communication between threads, based on the new objct locks, in my blog:
Thread Synchronization with Guarded Blocks in Delphi
In a note regarding the deprecated methods TThread.Suspend and
TThread.Resume, The Embarcadero DocWiki for Delphi
recommends that “thread
synchronization techniques should be
based on SyncObjs.TEvent and
SyncObjs.TMutex.“ There is, however,
another synchronization class
available since Delphi 2009: TMonitor.
It uses the object lock which has been
introduced in this version ...
public properties of the TWorker class MUST have get and set methods, so you can use a Tcriticalsection to give the values of the properties. Otherwise, you´d be having thread-safe issues. Your example seems ok, but in the real world, with thousands of threads accessing to the same value would result in an read error. Use critical sections.. and you wouldn´t have to use any Synchronize. This way you avoid going to the message queues of windows and improve performance. Besides, if you use this code in a windows service app, (where windows messages aren´t allowed), this example wouldn´t work. The synchronize method doesn´t work unless there´s access to the windows message queue.
Solved!! (answer taken from the question)
The fixes made for this problem where two fold.
First remove the syncronization call in the TWorker SendBossResult method.
Second add a fProcessWorkerResult CritialSection to TBoss class. Create and Free this in create/destroy of the TBoss. In the ProcessWorkerResults method call fProcessWorkerResult.Enter and fProcessWorkerResult.leave around the code which needs to be safe from multiple worker results streaming in.
The above was the conclusion after Kens code and follow up comment. Many thanks kind sir, hats off to you!.
I'm getting an exception in an OnTimer event handler (TTimer) that when executed increments an integer variable in the parent form. The timers need to be able to access an incremented integer used as an id.
My first question is: How can I tell in Delphi 2007 which code is running in which thread? Is there a way in debug mode to inspect this so I can determine for sure?
Secondly, if I need to access and modify variables in a parent form from another thread, what is the best way to do that? It seems like sometimes Delphi allows me to access these variables "incorrectly" without giving an exception and other times it does give an exception.
Just to be sure: On one hand you are talking about a timer event, on the other about multithreading. Those are two totally different ways of running code in parallel.
A timer will always be run in the main thread. It should be safe there to access everything that was created and is being used in the main thread. In fact, a timer event can only occur, when no other main thread code is running, because it needs the application's message handler to process the timer message. So it is either outside of any event handling code or when one of your event handlers calls Application.ProcessMessages.
A thread is very different from this. In this case, the code in different threads runs independently from each other. If running on a multi-processor machine (or multi core), it is even possible they truly run in parallel. There are quite a few issues you may have this way, in particular the Delphi VCL (up and including Delphi XE) is not thread save, so calls to any VCL class must only be done from the main thread (there are a few exceptions to this rule).
So, please first clarify whether you are talking about timers or true multithreading, before expecting any useful answers.
How can I tell in Delphi 2007 which
code is running in which thread? Is
there a way in debug mode to inspect
this so I can determine for sure?
You can set a breakpoint and when execution stops look at the threads debug window. Double click on each thread to see its callstack in the callstack debug window. You can also use the Win32 function GetCurrentThreadId to find out about the current thread (e.g. for logging, or to determine if the current thread is the main thread etc).
Since you are not showing any code it is hard to be more specific. Just to be sure: code in a timer event handler is not getting executed in a different thread. You won't have concurrent-access issues if you are just using timers, not real background threads.
Secondly, if I need to access and
modify variables in a parent form from
another thread, what is the best way
to do that? It seems like sometimes
Delphi allows me to access these
variables "incorrectly" without giving
an exception and other times it does
give an exception.
If you really are in another thread and access a shared variable you can see all sorts of things happening if you don't protect that access. It might work ok most of the time or you get strange values. If you just want to modify an integer in a thread-safe manner, look at InterlockedIncrement. Otherwise you could use a critical section, mutex, monitor... JEDI has some useful classes in the JclSynch unit for that.
You are asking two questions, so I'll answer them in two answers.
Your first question is about using TTimers; those always run in the main thread.
Most likely, your exception is an access violation.
If it is, it is usually caused by either of these:
a- your parent form is already
destroyed when your TTimer fires.
b- your do not have a reference yet to
your parent form when your TTimer
fires.
b is easy: just check if your reference is nil.
a is more difficult and depends on how you reference your parent form.
Basically you want to make sure your reference gets nil when the parent is being destroyed or removed.
If you reference your parent form through a global variable (in this example through Form2), then you should have TForm2 make the Form2 variable nil using the OnDestroy event like this:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm2 = class(TForm)
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormDestroy(Sender: TObject);
begin
Form2 := nil;
end;
end.
If you are using a field reference to your parent form (like FMyForm2Reference), then you should use add a Notification method like this:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Unit2;
type
TForm1 = class(TForm)
private
FMyForm2Reference: TForm2;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) then
if (AComponent = FMyForm2Reference) then
FMyForm2Reference := nil;
end;
end.
Regards,
Jeroen Pluimers
You are asking two questions, so I'll answer them in two answers.
Your second question is about making sure only 1 thread accessing 1 variable in a form at a time.
Since the variable is on a form, the best way is to use the Synchronize method for this.
There is an excellent example about this which that ships with Delphi, it is in the thrddemo.dpr project, where the unit in SortThds.pas has this method that shows how to use it:
procedure TSortThread.VisualSwap(A, B, I, J: Integer);
begin
FA := A;
FB := B;
FI := I;
FJ := J;
Synchronize(DoVisualSwap);
end;
Good luck,
Jeroen Pluimers