Delphi call a function in anonymous thread? - multithreading

I have to call a function in a anonymous thread in a while
my sample function is like this, just for print output:
function processPureTmFrame(rowFrame : string;tmDataGroupRef:string ):string;
TThread.Synchronize(nil,
procedure
begin
form2.Memo1.Lines.Add( tmSlitFrame );
end
);
end;
when i call the function like this :
code1
while tmBody.Length>0 do
begin
tmBodyFrameLength := ((hextodec( copy(tmBody,11,2) )+6)*2)+2;
tmSplitFrame := copy(tmBody , 1 , tmBodyFrameLength );
delete( tmBody, 1, tmBodyFrameLength );
myThread := TThread.CreateAnonymousThread(
procedure
begin
processPureTmFrame( tmSplitFrame , tmDataGroupRef );
end);
myThread.Start;
end;
in the first cycle of loop the output is missing
but when i call my code without thread every thing is ok!!
code2
while tmBody.Length>0 do
begin
tmBodyFrameLength := ((hextodec( copy(tmBody,11,2) )+6)*2)+2;
tmSplitFrame := copy(tmBody , 1 , tmBodyFrameLength );
delete( tmBody, 1, tmBodyFrameLength );
processPureTmFrame( tmSplitFrame , tmDataGroupRef );
end;
the correct output must be like this
0851C007000C010100000007581850C001F116
0836C0BE001003627169DCA200000000000090D72AACAF
0814C0B6001C03197169DCA31901E2041211131D001F00001F1E1C1F1F1E1E1E0077AA
0814C0B7001E03197169DCA31902FE00540F0000000000000000000000000000000000E238
0814C0B8000B03197169DCA31903FE01384E
0817C0B9000D05017169DCA3E6010190B03F042D
0852C000000B036200000000FAFFFFBF16A3
0852C001000B036200000001F4FF00000000
but when call in thread(code 1) its is like
0836C0BE001003627169DCA200000000000090D72AACAF
0814C0B6001C03197169DCA31901E2041211131D001F00001F1E1C1F1F1E1E1E0077AA
0814C0B7001E03197169DCA31902FE00540F0000000000000000000000000000000000E238
0814C0B8000B03197169DCA31903FE01384E
0817C0B9000D05017169DCA3E6010190B03F042D
0852C000000B036200000000FAFFFFBF16A3
0852C001000B036200000001F4FF00000000
without thread(code 2) output is ok
Note #1: I don't get any error like:
System Error. Code:1400. Invalid window handle or any thing else
Note #2: as I said just first cycle of while not sending to the new threads. Other lines are sending and processing just fine!

The problem is that the anonymous method captures variables. Because the variable is captured, its value changes during the main loop. Essentially all the threads share the same variable. The threads run in parallel with the main loop and there are no ordering constraints. So it's perfectly possible that the main loop modifies the captured variable before one of your threads has a chance to use the value.
Your code would work with value capture (as opposed to variable capture). Value capture is not supported directly but the same effect is easy to simulate. See Anonymous methods - variable capture versus value capture.
I would comment though that this threading code is going to be slower than the serial code. What are you hoping to achieve?

Related

Why doesn't this Synchronize procedure lock the main thread

I have following code:
TThread.Synchronize(nil,
procedure
begin
with Scope.New(TManualCaptchaForm.Create(img)) do
if It.ShowModal() = mrOk then
res := It.edtResolved.Text;
end
);
Why does the form appear several times when multiple TThreads use this procedure for synchronization? I know a workaround, and there is nothing unusual (e. g. no other "hand-made" ways to sync with main thread), but why am I not experiencing a lock?
Yes, Scope.New is kinda smart-pointer, BUT only i see TThread.Synchronize and passed closure? Documentation says that any method/closure passed to TThread.Synchronize will be execute inside main thread. Obviously, ShowModal must block main thread, but it didn't do that. As for me, it's very strange that any other window start behave as main thread and pump synchronization queue.
P. s. almost MVP:
TThread.Synchronize(nil,
procedure
var Form: TForm1;
begin
Form := TForm1.Create(nil);
try
Form.ShowModal();
finally
Form.Free;
end;
end
);
Run this code in 2+ threads and see bug. Anyway, now I know that synchronization queue pumped by any window message loop, not just by main form.
Btw, my question was "Why TThread.Synchronize behave so unclear/not logically?", not about my own code.
The fundamental misunderstanding here is that because .ShowModal is a blocking call you expect that it also suspends message processing. It does not. When you create a modal window the modal window takes over message processing - it must do this or the window would not function. The main thread is still processing the message loop, it is just doing it in a different context.
If you want to think of it this way, ShowModal behaves a lot like Application.ProcessMessages. This has nothing to do with Synchronize. If you examine the code for ShowModal you find :
{ ... }
Show;
try
SendMessage(Handle, CM_ACTIVATE, 0, 0);
ModalResult := 0;
{ *** Here is your message loop *** }
repeat
Application.HandleMessage;
if Application.Terminated then ModalResult := mrCancel else
if ModalResult <> 0 then CloseModal;
until ModalResult <> 0;
{ *** ------------------------- *** }
Result := ModalResult;
SendMessage(Handle, CM_DEACTIVATE, 0, 0);
if GetActiveWindow <> Handle then ActiveWindow := 0;
finally
Hide;
end;
{ ... }
If you want to prevent reentrance here you have to devise your own explicit method to do so.

Delphi Thread and TCriticalSection

I encounter some strange behaviors with threads so I guess there is something I'm doing wrong or something I don't understand.
My application (Delphi Berlin) has two processes : a service and a console app. They communitcate via socket (Indy).
Each process has a thread dedicated to communication.
I use TCriticalSection when I need to read/write variables used by main thread and by communication Thread.
I also make intensive usage of log. The log can be written (one log file by process) by main thread and by communication thread.
So what I'm doing when I want to write a trace in log file is to use a variable TCriticalSection to prevent main thread and commmunication thread to write to the log file at the same time:
Procedure TApp.trace(logLevel : byte; procName , pi_str: string);
var F: textfile;
LogFileName: String;
vl_log : Boolean;
vc_LogHeader : String;
th, thcurrent : TTh;
begin
if GetLog() then begin // False if log is deactivated
for th := Low(TTh) to High(TTh) do begin
if TThread.CurrentThread.ClassName = ThreadsLog.Name[th] then begin
thcurrent := th;
break;
end;
end;
if ThreadsLog.LogLevel[thcurrent] < logLevel then exit;
LogFileName := gc_tmp + WinProc.Name[WHO_AM_I] + '.log';
vc_LogHeader := '[' + GetLogTime + ' ' + ThreadsLog.Name[thcurrent] + ' ' + procName + ' ' + IntToStr(logLevel) + ']';
if Length(vc_LogHeader) < 60 then vc_LogHeader := vc_LogHeader + StringOfChar (' ', 60 - Length(vc_LogHeader) );
LockTrace.Acquire;
try
try
{$IFDEF MACOS}
AssignFile(F, LogFileName, CP_UTF8);
{$ELSE}
AssignFile(F, LogFileName);
{$ENDIF}
if FileExists(LogFileName) then Append(F) else Rewrite(F);
{$IFDEF MACOS}
Writeln(F, UTF8String(vc_LogHeader + AnsiString(pi_str)));
{$ELSE}
Writeln(F, vc_LogHeader + pi_str);
{$ENDIF}
CloseFile(F);
except
on e : exception do begin
dbg(LogFileName + ' ' + e.Message);
end;
end;
finally
lockTrace.Release;
end;
end;
end;
function TApp.GetLog() : boolean;
begin
gl_logLock.Acquire;
try
result := gl_log;
finally
gl_logLock.Release;
end;
end;
However sometimes, some lines are not written to the file.
But dbg(LogFileName + ' ' + e.Message) does not execute cause it is supposed to write in another log file and this file stays empty. So no exception seems to be fired.
Is it possible to use TCriticalSection this way ?
What I understand about TCriticalSection, is that it puts a lock so others threads trying to put their own lock have to wait until it is released. Is that right ?
I guess I can use one variable or several variables TCriticalSection. If I use only one variable, there will be more cases where a lock exists so more time to wait. If I use one TCriticalSection per shared variable, there will be less locks so better performances. Is it right ?
Thanks for any correction or clarification.
There are many problems with your code, not all thread / critical section related.
function TApp.GetLog() : boolean;
begin
gl_logLock.Acquire;
try
result := gl_log;
finally
gl_logLock.Release;
end;
end;
The above lock code is useless it doesn't provide any protection whatsoever. Reading a boolean variable is already atomic. It's also symptomatic of a common misunderstanding of how to make code thread-safe.
Locks are intended to protect access to data.
The above pattern is often incorrectly used to protect access to an object.
But once the calling code is able to start using the object, you're already outside the lock.
I.e the underlying data of the object is no longer protected from concurrent thread access.
for th := Low(TTh) to High(TTh) do begin
if TThread.CurrentThread.ClassName = ThreadsLog.Name[th] then begin
thcurrent := th;
break;
end;
end;
if ThreadsLog.LogLevel[thcurrent] < logLevel then exit;
In the above, if the loop ever ends without the if condition evaluating to True, thcurrent will be uninitialised leading to undefined behaviour. Anything from AV exceptions to things just not behaving as you'd expect.
Quite possibly ThreadsLog.LogLevel[thcurrent] < logLevel could evaluate to True (and Exit) without triggering an AV for some undefined values of thcurrent.
Also note that looping through your threads and doing string comparisons is a pretty inefficient way to check your current thread. It's not clear what you're trying to achieve, but you should be able to figure certain things out simply from the current thread id.
You say dbg(LogFileName + ' ' + e.Message); is not called. Well there are many reasons it might not be called. You'll have to figure out which (1 or multiple) apply.
You could Exit early.
GetLog() might return False.
Any exception before the try..except block won't get there.
If you've disabled IO errors, an exception won't be raised by old-style file operations. You would have to manually check them using IOResult.
And of course dbg might be called, but could itself also fail in some way.

Using the Delphi XE7 Parallel Library

I have a time consuming routine which I'd like to process in parallel using Delphi XE7's new parallel library.
Here is the single threaded version:
procedure TTerritoryList.SetUpdating(const Value: boolean);
var
i, n: Integer;
begin
if (fUpdating <> Value) or not Value then
begin
fUpdating := Value;
for i := 0 to Count - 1 do
begin
Territory[i].Updating := Value; // <<<<<< Time consuming routine
if assigned(fOnCreateShapesProgress) then
fOnCreateShapesProgress(Self, 'Reconfiguring ' + Territory[i].Name, i / (Count - 1));
end;
end;
end;
There is really nothing complex going on. If the territory list variable is changed or set to false then the routine loops around all the sales territories and recreates the territory border (which is the time consuming task).
So here is my attempt to make it parallel:
procedure TTerritoryList.SetUpdating(const Value: boolean);
var
i, n: Integer;
begin
if (fUpdating <> Value) or not Value then
begin
fUpdating := Value;
n := Count;
i := 0;
TParallel.For(0, Count - 1,
procedure(Index: integer)
begin
Territory[Index].Updating := fUpdating; // <<<<<< Time consuming routine
TInterlocked.Increment(i);
TThread.Queue(TThread.CurrentThread,
procedure
begin
if assigned(fOnCreateShapesProgress) then
fOnCreateShapesProgress(nil, 'Reconfiguring ', i / n);
end);
end
);
end;
end;
I've replaced the for-loop with a parallel for-loop. The counter, 'i' is locked as it is incremented to show progress. I then wrap the OnCreateShapeProgress event in a TThread.Queue, which will be handled by the main thread. The OnCreateShapeProgress event is handled by a routine which updates the progress bar and label describing the task.
The routine works if I exclude the call to the OnCreateShapeProgress event. It crashes with an EAurgumentOutOfRange error.
So my question is simple:
Am I doing anything anything stupid?
How to do you call an event handler from within a TParallel.For loop or TTask?
The most obvious problem that I can see is that you queue to the worker thread.
Your call to TThread.Queue passes TThread.CurrentThread. That is the very thread on which you are calling TThread.Queue. I think it is safe to say that you should never pass TThread.CurrentThread to TThread.Queue.
Instead, remove that parameter. Use the one parameter overload that just accepts a thread procedure.
Otherwise I'd note that the incrementing of the progress counter i is not really handled correctly. Well, the incrementing is fine, but you then read it later and that's a race. You can report progress out of order if thread 1 increments before thread 2 but thread 2 queues progress before thread 1. Solve that by moving the counter increment code to the main thread. Simply increment it inside the queued anonymous method. Added bonus to that is you no longer need to use an atomic increment since all modifications are on the main thread.
Beyond that, this QC report seems rather similar to what you report: http://qc.embarcadero.com/wc/qcmain.aspx?d=128392
Finally, AtomicIncrement is the idiomatic way to perform lock free incrementing in the latest versions of Delphi.

Delphi thread return value

Can someone explain to me how I get a return value from myThread calling function test?
function test(value: Integer): Integer;
begin
Result := value+2;
end;
procedure myThread.Execute;
begin
inherited;
test(Self.fParameters);
end;
procedure getvaluefromthread();
var
Capture : myThread;
begin
list := TStringList.Create;
Capture := myThread.Create(False);
Capture.fParameters := 2;
Capture.Resume;
end;
Declare a class derived from TThread.
Add a field, or multiple fields, to contain the result value or values.
Set the result value field(s) in the overridden Execute method.
When the thread has finished, read the result from the thread instance.
As Remy points out, if you wish to return just a single Integer value, then you can use the ReturnValue property of TThread. Use this in just the same way as described above. Note that the value placed in ReturnValue is the value returned by the underlying OS thread.
You can listen for OnTerminate to find out when thread is done. Or call WaitFor.
Note that you set the thread's parameters after it starts running. Either create the thread suspended, or pass the parameters to the constructor. Also, you should use Start rather than Resume. The latter is deprecated.

How can I terminate my threads with blocking functions/procedures?

I use TThread in my application and I have a LOT of functions that I would like to use inside of it.
Functions that I use, take time to complete, so it's not ideal to use them in threads. That's why I was wondering if there's a way other than just copy & paste the function/procedure and then put (maybe inject) my terminated flags into the function.
I don't want to use TerminateThread API!
A short example:
procedure MyProcedure;
begin
// some work that takes time over a few lines of code
// add/inject terminated flag?!
// try... finally...
end;
procedure TMyThread.Execute;
begin
MyProcedure;
// or copy and paste myprocedure
end;
So is there an efficient way to write procedures/functions that help me with the terminated flag? Also the procedures/functions should be global so other functions/procedures can call them too.
One option is to introduce a callback method into your procedure call.
If the callback method is Assigned (when called from a thread) make the call and take action.
When calling MyProcedure from elsewhere, pass nil to the procedure.
Type
TAbortProc = function : boolean of object;
procedure MyProcedure( AbortProc : TAbortProc);
begin
//...
if (Assigned(AbortProc) and AbortProc) then
Exit;
//...
end;
function MyThread.AbortOperation : Boolean;
begin
Result := Terminated;
end;
The reason why I avoid passing the thread reference instead of a callback method, is to hide the thread logic (and dependency) from MyProcedure.

Resources