How do I access the 'NameThreadForDebugging' in a delphi Thread in Delphi 2010 ?
type
TMyThread = class(TThread)
protected
procedure Execute; override;
procedure UpdateCaption;
end;
implementation
procedure TMyThread.UpdateCaption;
begin
Form1.Caption := 'Name Thread For Debugging';
// how I get 'TestThread1' displayed in the caption
end;
procedure TMyThread.Execute;
begin
NameThreadForDebugging('TestThread1');
Synchronize(UpdateCaption);
Sleep(5000);
end;
The NameThreadForDebugging function is, as its name suggests, for debugging only. If you want to keep track of the name for other purposes, then reserve a field in the thread object and store the name there. Use that field for naming the thread and for populating your form's caption on demand.
There is no API for retrieving a thread's name because threads don't have names at the API level. NameThreadForDebugging raises a special exception that the IDE recognizes as the "name this thread" exception. It sees the exception (since it's a debugger), makes a note about the thread's name in its own internal debugging data structures, and then allows the application to continue running. The application catches and ignores the exception.
That data transfer is one-way, though. The application can send information to the debugger via an exception, but the debugger can't send data back. And the OS is oblivious to everything. To the OS, it's just like any other exception.
To do what you ask, you need to store the Name inside your thread class where you can access it, eg:
type
TMyThread = class(TThread)
protected
FName: String;
procedure Execute; override;
procedure UpdateCaption;
end;
procedure TMyThread.UpdateCaption;
begin
Form1.Caption := FName;
end;
procedure TMyThread.Execute;
begin
FName := 'TestThread1';
NameThreadForDebugging(FName);
Synchronize(UpdateCaption);
Sleep(5000);
end;
The unit DebugThreadSupport on Code Central example ID: 21893, Named Pipes, shows how to set thread name in older versions of Delphi.
AFAICS Delphi supports settings the name only. You'll have to call some windows API function to get the name.
Related
I have to modify and change some visual components in a thread and as you know it's not safe to doing this.
My question is how to write a completely thread-safe code? It is possible? if it is then can you please give me a simple example?
my code that is not threadsafe:
type
tMyWorkerThread = class(TThread)
public
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure tMyWorkerThread.Execute;
begin
//codes
//working with visual components
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyWorkerThread.Create(false);
end;
Thank you.
Writing a thread safe code in Delphi involves the basic care you would have in any other language, which means to deal with race conditions. A race condition happens when different threads access the same data. A good way to deal with that is to declare an instance of TCriticalSection and wrap the dangerous code in it.
The code below shows a getter and a setter of a property that, by hypotesis, has a race condition.
constructor TMyThread.Create;
begin
CriticalX := TCriticalSection.Create;
end;
destructor TMyThread.Destroy; override;
begin
FreeAndNil(CriticalX);
end;
function TMyThread.GetX: string;
begin
CriticalX.Enter;
try
Result := FX;
finally
CriticalX.Leave;
end;
end;
procedure TMyThread.SetX(const value: string);
begin
CriticalX.Enter;
try
FX := Value;
finally
CriticalX.Leave;
end;
end;
Notice the use of a single instance of TCriticalSection (CriticalX) to serialize the access to the data member FX.
However, with Delphi you have an aditional consideration! VCL is not thread safe, so in order to avoid VCL race conditions, any operation that results in screen changing must run in the main thread. You get that by calling such a code inside a Synchronize method. Considering the class above, you should do something like this:
procedure TMyThread.ShowX;
begin
Synchronize(SyncShowX);
end;
procedure TMyThread.SyncShowX;
begin
ShowMessage(IntToStr(FX));
end;
If you have Delphi 2010 or later, there is an easier way that makes use of anonymous methods:
procedure TMyThread.ShowX;
begin
Synchronize(procedure begin
ShowMessage(IntToStr(FX));
end);
end;
I hope this helps!
You should only access VCL objects from main VCL thread.
Some reading methods (property getters) do work from other threads in practice - but you have to prove it in advance reading VCL sources for the specific Delphi build. Or not use it.
PS: Synchronize method runs given procedure in main VCL thread, pausing the caller thread, which may lead to a deadlock, if main thread was also blocked.
Read more: (actually making this answer to list some links)
http://www.michael-puff.de/Programmierung/Delphi/Code-Snippets/VCLThreadDemo.shtml
http://www.drbob42.com/uk-bug/hood-04.htm
http://delphi.about.com/od/kbthread/a/thread-gui.htm
Is it better to use TThread's "Synchronize" or use Window Messages for IPC between main and child thread?
Delphi 6 : breakpoint triggered on non-VCL thread stops main thread repaints
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devwin32/win32_mthreadusemainthread_xml.html
Simplifying VCL thread wrapper code
Update a VCL component from CreateAnonymousThread
http://thaddy.co.uk/threads/ - the mirror of "Multithreading - The Delphi Way" by Martin Harvey
http://otl.17slon.com/ - new Delphi approach to threading
My problem solved with Synchronize!
type
tMyWorkerThread = class(TThread)
public
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure tMyWorkerThread.Execute;
begin
//codes that takes long time
Synchronize(procedure begin
//working with visual components
end
);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyWorkerThread.Create(false);
end;
Thank you all for helping me.
I am using mutiple connection using ODBC. In whole project I am using the same connection, but create, use and destory TQuery object. Now I am going to use connection in threads and came to know Delphi BDE provides TSession Class for that. I want to know how to use TSession for concurrent operation, please provide code sample for that if possible.
While I agree that the BDE is old, it is possible to create thread-safe access to the database using the BDE and TSessions.
Consider this. When two copies of the same application are running at the same time, the database engine or database server distinguishes between the two instances for the purpose of record and table locking. This distinction is possible because each application uses a separate connection, or in the case of the BDE, session.
The session is represented by a TSession instance. In single threaded projects the TSession is created for you. If you want to connect to the BDE with two or more threads, each should have its own TSession.
Using multiple TSessions is demonstrated here, in this really old code example that I dug up (it is old, and I would do it differently today, but you asked for it). The trick is that each session needs to have the same network directory and have a unique private directory. Here is the TThread descendant:
type
TWriteData = class(TThread)
private
FSQL: String;
FFileName: String;
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: Boolean; const SQL: String;
const FileName: String); override; overload;
end;
Here is the overridden constructor:
constructor TWriteData.Create(CreateSuspended: Boolean;
const SQL: String; const FileName: String);
begin
inherited Create(True);
FSQL := SQL;
FFileName := String;
end;
And here is the execute method. Importantly, the TSession.PrivateDir is set to a unique directory name (based on the ThreadID). Could also use a GUID, or some other value, as long as it is unique. Note also that Session1 is a TSession component on the data module, and Query1 is a TQuery that uses a TDatabase (Database1), which in turn uses Session1. Session is a variable declared in the Bde.DBTables unit. This variable refers to the default TSession that the BDE creates for the BDE TDataSets that are active in the primary thread of execution.
procedure TWriteData.Execute;
var
DataMod: TDataModule1;
AppDir: String;
begin
AppDir := ExtractFilePath(Application.ExeName);
DataMod := TDataModule1.Create(nil);
try
with DataMod do
begin
//All sessions need a unique private directory
Session1.PrivateDir := AppDir + IntToStr(Self.ThreadID);
//All sessions share a common network control file
Session1.NetFileDir := Session.NetFileDir;
ForceDirectories(Session1.PrivateDir);
try
Query1.SQL.Text := FSQL;
ClientDataSet1.Open;
ClientDataSet1.SaveToFile(AppDir + FFileName);
ClientDataSet1.Close;
finally
SysUtils.RemoveDir(Session1.PrivateDir);
end; //try
end; //begin
finally
DataMod.Free;
end;
end;
I hope this helps.
I have some inherited code for opening IE, this is short version :
procedure OpenIE(URL: OleVariant; FieldValues: string = '');
var ie : IWebBrowser2;
begin
ie := CreateOleObject('InternetExplorer.Application') as IWebBrowser2;
ie.Navigate2(URL, Flags, TargetFrameName, PostData, Headers);
ShowWindow(ie.HWND, SW_SHOWMAXIMIZED);
ie.Visible := true;
...
end;
Since CreateOleObject takes a long time to execute I would like to have one "prepared" IE for the first run.
For example in Main FormCreate to call CreateOleObject, then for 1st call of OpenIE to use "IE" object already created.
For 2nd, 3rd ... call of OpenIE - just usual call
ie := CreateOleObject
When I try to code it, I get some threads and marshaling errors, I am newbie in this area. What would be proper way to do this (some small code example would be great) ?
Thanks in advance.
Perhaps you are creating the browser instance in a different thread from which you then issue subsequent calls. The following trivial code works exactly as expected:
type
TMainForm = class(TForm)
ShowBrowser: TButton;
procedure FormCreate(Sender: TObject);
procedure ShowBrowserClick(Sender: TObject);
private
FBrowser: Variant;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
FBrowser := CreateOleObject('InternetExplorer.Application');
end;
procedure TMainForm.ShowBrowserClick(Sender: TObject);
begin
FBrowser.Navigate('http://stackoverflow.com');
ShowWindow(FBrowser.HWND, SW_SHOWMAXIMIZED);
FBrowser.Visible := True;
end;
I'm not using IWebBrowser2 because I don't have the import unit handy. But that won't change anything – your problems will not be related to early/late binding.
Obviously FormCreate runs in the GUI thread. And ShowBrowserClick is a button OnClick event handler. And so it runs in the main GUI thread.
If you are calling your OpenIE function from a thread other than the GUI thread, that would explain your errors. If you access the browser on a thread other than the one on which it was created, you will receive an EOleSysError with message The application called an interface that was marshalled for a different thread.
Finally, a word of advice when asking questions. If you receive an error message, make sure you include that exact error message in your question. Doing so makes it much more likely we can provide good answers.
After reading the articles "Simmering Unicode, bring DPL to a boil" and "Simmering Unicode, bring DPL to a boil (Part 2)" of "The Oracle at Delphi" (Allen Bauer), Oracle is all I understand :)
The article mentions Delphi Parallel Library (DPL), lock free data structures, mutual exclusion locks and condition variables (this Wikipedia article forwards to 'Monitor (synchronization)', and then introduces the new TMonitor record type for thread synchronization and describes some of its methods.
Are there introduction articles with examples which show when and how this Delphi record type can be used? There is some documentation online.
What is the main difference between TCriticalSection and TMonitor?
What can I do with the Pulse and PulseAllmethods?
Does it have a counterpart for example in C# or the Java language?
Is there any code in the RTL or the VCL which uses this type (so it could serve as an example)?
Update: the article Why Has the Size of TObject Doubled In Delphi 2009? explains that every object in Delphi now can be locked using a TMonitor record, at the price of four extra bytes per instance.
It looks like TMonitor is implemented similar to Intrinsic Locks in the Java language:
Every object has an intrinsic lock
associated with it. By convention, a
thread that needs exclusive and
consistent access to an object's
fields has to acquire the object's
intrinsic lock before accessing them,
and then release the intrinsic lock
when it's done with them.
Wait, Pulse and PulseAll in Delphi seem to be counterparts of wait(), notify() and notifyAll() in the Java programming language. Correct me if I am wrong :)
Update 2: Example code for a Producer/Consumer application using TMonitor.Wait and TMonitor.PulseAll, based on an article about guarded methods in the Java(tm) tutorials (comments are welcome):
This kind of application shares data
between two threads: the producer,
that creates the data, and the
consumer, that does something with it.
The two threads communicate using a
shared object. Coordination is
essential: the consumer thread must
not attempt to retrieve the data
before the producer thread has
delivered it, and the producer thread
must not attempt to deliver new data
if the consumer hasn't retrieved the
old data.
In this example, the data is a series of text messages, which are shared through an object of type Drop:
program TMonitorTest;
// based on example code at http://download.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
type
Drop = class(TObject)
private
// Message sent from producer to consumer.
Msg: string;
// True if consumer should wait for producer to send message, false
// if producer should wait for consumer to retrieve message.
Empty: Boolean;
public
constructor Create;
function Take: string;
procedure Put(AMessage: string);
end;
Producer = class(TThread)
private
FDrop: Drop;
public
constructor Create(ADrop: Drop);
procedure Execute; override;
end;
Consumer = class(TThread)
private
FDrop: Drop;
public
constructor Create(ADrop: Drop);
procedure Execute; override;
end;
{ Drop }
constructor Drop.Create;
begin
Empty := True;
end;
function Drop.Take: string;
begin
TMonitor.Enter(Self);
try
// Wait until message is available.
while Empty do
begin
TMonitor.Wait(Self, INFINITE);
end;
// Toggle status.
Empty := True;
// Notify producer that status has changed.
TMonitor.PulseAll(Self);
Result := Msg;
finally
TMonitor.Exit(Self);
end;
end;
procedure Drop.Put(AMessage: string);
begin
TMonitor.Enter(Self);
try
// Wait until message has been retrieved.
while not Empty do
begin
TMonitor.Wait(Self, INFINITE);
end;
// Toggle status.
Empty := False;
// Store message.
Msg := AMessage;
// Notify consumer that status has changed.
TMonitor.PulseAll(Self);
finally
TMonitor.Exit(Self);
end;
end;
{ Producer }
constructor Producer.Create(ADrop: Drop);
begin
FDrop := ADrop;
inherited Create(False);
end;
procedure Producer.Execute;
var
Msgs: array of string;
I: Integer;
begin
SetLength(Msgs, 4);
Msgs[0] := 'Mares eat oats';
Msgs[1] := 'Does eat oats';
Msgs[2] := 'Little lambs eat ivy';
Msgs[3] := 'A kid will eat ivy too';
for I := 0 to Length(Msgs) - 1 do
begin
FDrop.Put(Msgs[I]);
Sleep(Random(5000));
end;
FDrop.Put('DONE');
end;
{ Consumer }
constructor Consumer.Create(ADrop: Drop);
begin
FDrop := ADrop;
inherited Create(False);
end;
procedure Consumer.Execute;
var
Msg: string;
begin
repeat
Msg := FDrop.Take;
WriteLn('Received: ' + Msg);
Sleep(Random(5000));
until Msg = 'DONE';
end;
var
ADrop: Drop;
begin
Randomize;
ADrop := Drop.Create;
Producer.Create(ADrop);
Consumer.Create(ADrop);
ReadLn;
end.
Now this works as expected, however there is a detail which I could improve: instead of locking the whole Drop instance with TMonitor.Enter(Self);, I could choose a fine-grained locking approach, with a (private) "FLock" field, using it only in the Put and Take methods by TMonitor.Enter(FLock);.
If I compare the code with the Java version, I also notice that there is no InterruptedException in Delphi which can be used to cancel a call of Sleep.
Update 3: in May 2011, a blog entry about the OmniThreadLibrary presented a possible bug in the TMonitor implementation. It seems to be related to an entry in Quality Central. The comments mention a patch has been provided by a Delphi user, but it is not visible.
Update 4: A blog post in 2013 showed that while TMonitor is 'fair', its performance is worse than that of a critical section.
TMonitor combines the notion of a critical section (or a simple mutex) along with a condition variable. You can read about what a "monitor" is here:
http://en.wikipedia.org/wiki/Monitor_%28synchronization%29
Any place you would use a critical section, you can use a monitor. Instead of declaring a TCriticalSection, you can simple create a TObject instance and then use that:
TMonitor.Enter(FLock);
try
// protected code
finally
TMonitor.Exit(FLock);
end;
Where FLock is any object instance. Normally, I just create a TObject:
FLock := TObject.Create;
i'm writing a delphi app that communicates with excel. one thing i noticed is that if i call the Save method on the Excel workbook object, it can appear to hang because excel has a dialog box open for the user. i'm using the late binding.
i'd like for my app to be able to notice when Save takes several seconds and then take some kind of action like show a dialog box telling this is what's happening.
i figured this'd be fairly easy. all i'd need to do is create a thread that calls Save and have that thread call Excel's Save routine. if it takes too long, i can take some action.
procedure TOfficeConnect.Save;
var
Thread:TOfficeHangThread;
begin
// spin off as thread so we can control timeout
Thread:=TOfficeSaveThread.Create(m_vExcelWorkbook);
if WaitForSingleObject(Thread.Handle, 5 {s} * 1000 {ms/s})=WAIT_TIMEOUT then
begin
Thread.FreeOnTerminate:=true;
raise Exception.Create(_('The Office spreadsheet program seems to be busy.'));
end;
Thread.Free;
end;
TOfficeSaveThread = class(TThread)
private
{ Private declarations }
m_vExcelWorkbook:variant;
protected
procedure Execute; override;
procedure DoSave;
public
constructor Create(vExcelWorkbook:variant);
end;
{ TOfficeSaveThread }
constructor TOfficeSaveThread.Create(vExcelWorkbook:variant);
begin
inherited Create(true);
m_vExcelWorkbook:=vExcelWorkbook;
Resume;
end;
procedure TOfficeSaveThread.Execute;
begin
m_vExcelWorkbook.Save;
end;
i understand this problem happens because the OLE object was created from another thread (absolutely).
how can i get around this problem? most likely i'll need to "re-marshall" for this call somehow...
any ideas?
The real problem here is that Office applications aren't intended for multithreaded use. Because there can be any number of client applications issuing commands through COM, those commands are serialized to calls and processed one by one. But sometimes Office is in a state where it doesn't accept new calls (for example when it is displaying a modal dialog) and your call gets rejected (giving you the "Call was rejected by callee"-error). See also the answer of Geoff Darst in this thread.
What you need to do is implement a IMessageFilter and take care of your calls being rejected. I did it like this:
function TIMessageFilterImpl.HandleInComingCall(dwCallType: Integer;
htaskCaller: HTASK; dwTickCount: Integer;
lpInterfaceInfo: PInterfaceInfo): Integer;
begin
Result := SERVERCALL_ISHANDLED;
end;
function TIMessageFilterImpl.MessagePending(htaskCallee: HTASK;
dwTickCount, dwPendingType: Integer): Integer;
begin
Result := PENDINGMSG_WAITDEFPROCESS;
end;
function ShouldCancel(aTask: HTASK; aWaitTime: Integer): Boolean;
var
lBusy: tagOLEUIBUSYA;
begin
FillChar(lBusy, SizeOf(tagOLEUIBUSYA), 0);
lBusy.cbStruct := SizeOf(tagOLEUIBUSYA);
lBusy.hWndOwner := Application.Handle;
if aWaitTime < 20000 then //enable cancel button after 20 seconds
lBusy.dwFlags := BZ_NOTRESPONDINGDIALOG;
lBusy.task := aTask;
Result := OleUIBusy(lBusy) = OLEUI_CANCEL;
end;
function TIMessageFilterImpl.RetryRejectedCall(htaskCallee: HTASK;
dwTickCount, dwRejectType: Integer): Integer;
begin
if dwRejectType = SERVERCALL_RETRYLATER then
begin
if dwTickCount > 10000 then //show Busy dialog after 10 seconds
begin
if ShouldCancel(htaskCallee, dwTickCount) then
Result := -1
else
Result := 100;
end
else
Result := 100; //value between 0 and 99 means 'try again immediatly', value >= 100 means wait this amount of milliseconds before trying again
end
else
begin
Result := -1; //cancel
end;
end;
The messagefilter has to be registered on the same thread as the one issuing the COM calls. My messagefilter implementation will wait 10 seconds before displaying the standard OLEUiBusy dialog. This dialog gives you the option to retry the rejected call (in your case Save) or switch to the blocking application (Excel displaying the modal dialog).
After 20 seconds of blocking, the cancel button will be enabled. Clicking the cancel button will cause your Save call to fail.
So forget messing around with threads and implement the messagefilter, which is the way
to deal with these issues.
Edit:
The above fixes "Call was rejected by callee" errors, but you have a Save that hangs. I suspect that Save brings up a popup that needs your attention (Does your workbook has a filename already?). If it is a popup that is in the way, try the following (not in a separate thread!):
{ Turn off Messageboxes etc. }
m_vExcelWorkbook.Application.DisplayAlerts := False;
try
{ Saves the workbook as a xls file with the name 'c:\test.xls' }
m_vExcelWorkbook.SaveAs('c:\test.xls', xlWorkbookNormal);
finally
{ Turn on Messageboxes again }
m_vExcelWorkbook.Application.DisplayAlerts := True;
end;
Also try to debug with Application.Visible := True; If there are any popups, there is a change you will see them and take actions to prevent them in the future.
Rather than accessing the COM object from two threads, just show the message dialog in the secondary thread. The VCL isn't thread-safe, but Windows is.
type
TOfficeHungThread = class(TThread)
private
FTerminateEvent: TEvent;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
procedure Terminate; override;
end;
...
constructor TOfficeHungThread.Create;
begin
inherited Create(True);
FTerminateEvent := TSimpleEvent.Create;
Resume;
end;
destructor TOfficeHungThread.Destroy;
begin
FTerminateEvent.Free;
inherited;
end;
procedure TOfficeHungThread.Execute;
begin
if FTerminateEvent.WaitFor(5000) = wrTimeout then
MessageBox(Application.MainForm.Handle, 'The Office spreadsheet program seems to be busy.', nil, MB_OK);
end;
procedure TOfficeHungThread.Terminate;
begin
FTerminateEvent.SetEvent;
end;
...
procedure TMainForm.Save;
var
Thread: TOfficeHungThread;
begin
Thread := TOfficeHungThread.Create;
try
m_vExcelWorkbook.Save;
Thread.Terminate;
Thread.WaitFor;
finally
Thread.Free;
end;
end;
Try calling CoInitializeEx with COINIT_MULTITHREADED since MSDN states:
Multi-threading (also called free-threading) allows calls to methods of objects created by this thread to be run on any thread.
'Marshalling' an interface from one thread to another can be done by using CoMarshalInterThreadInterfaceInStream to put the interface into a stream, move the stream to the other thread and then use CoGetInterfaceAndReleaseStream to get the interface back from the stream. see here for an example in Delphi.
Lars' answer is along the right lines I think. An alternative to his suggestion is to use the GIT (Global Interface Table), which can be used as a cross-thread repository for interfaces.
See this SO thread here for code for interacting with the GIT, where I posted a Delphi unit that provides simple access to the GIT.
It should simply be a question of registering your Excel interface into the GIT from your main thread, and then getting a separate reference to the interface from within your TOfficeHangThread thread using the GetInterfaceFromGlobal method.