Jump to content
t2000

Sample for TWaitFor (or something similar)

Recommended Posts


I am in a (foreign) communication thread. I must call a function in the MainThread and send the result back.


My approach:

 

I put a record with TFunc, Para in a TOmniValue and then in a TOmniBlockingCollection.
I call: PostThreadMessage( System.MainThreadID, sms_Synchronize, 0, 0);
And here (in the comm thread) I have to wait. Wait for a signal. ?? perhaps CreateEvent ??

 

In the main thread main messageloop I get the message.

I take the record from the TOmniBlockingCollection and execute the function TFunc(para).

Now I have to put the result in ... what? And signal the origin thread

Is TWaitFor the right choice?

Can you give me a sample? The WaitFor-Example from OmniThreadLibrary (test_59_TWaitFor) is too complex.

 

Or is there a better way? Without use (my own) main thread message loop?

 

Many thanks
Thomas
 

(The function must be run in the main thread, because it uses a windows com server object and this don't work in any threads. I have synchronize/serialize the calls from the communication thread)

Share this post


Link to post
14 hours ago, t2000 said:

The function must be run in the main thread, because it uses a windows com server object and this don't work in any threads. I have synchronize/serialize the calls from the communication thread

COM objects will work in a sub-thread if they are created after calling WinAPI.ActiveX.CoInitialize(nil) and destroyed before calling WinAPI.ActiveX.CoUnintialize, in that thread.

Share this post


Link to post

No, WaitFor is not the right choice.

 

Just use TThread.Queue and execute the code in the main thread. Something like:

 

res := CalculateTheResult();
TThread.Queue(nil,
  procedure
  begin
    // This procedure will execute in the main thread.
    // The address of the 'res' variable will be automatically captured so you can use its value here.
    ProcessInMainThread(res);
  end);

 

Share this post


Link to post

I think this should be TThread.Synchronize because the code needs to wait for the result.

Share this post


Link to post

@Lars Fosdal Is CoInitialize fast enough? This is a communication thread (of a REST Microservice), which is called very often.

 

I will test the TThread.Queue approach.
It looks way too easy :classic_wink:

But I have to use:
 

res := ProcessInMainThread(para);

 

Share this post


Link to post

Use a worker thread which stays active and take tasks through a queue.

Share this post


Link to post

Yes, that could work.
TThread.Queue doesn't work, because I need the result.

TThread.Synchronize doesn't work at all. I do not know why.

I have a BackgroundWorker for some things to do (parallel and without a function result)
Now I can try to create a Worker Thread. But

 

the most important is, I need the result. I need it IN the communication thread. The communication thread has to wait.

 

Share this post


Link to post

If Synchronize is not working (can't tell why without your code), use Queue with a record containing (data, result, flag) and wait for main thread to set the flag. Like this:

var
  res, data: ...
  flag: boolean;

flag := false;

TThread.Queue(nil,
  procedure
  begin
    res := Process(data);
    flag := true;
  end);

while not flag do Sleep(0);

Or make 'flag' an event and wait on it. 

Share this post


Link to post

I have a command line application.

Here are then Synchronize Test, the application loop and the ProcessMessage function

function TServerApp.SynchronizeAndWait( AFuncToCall: TFunc<TOmniValue,TOmniValue>; AData: TOmniValue): TOmniValue;
var
  CallRec : TFuncAndDataRec;
  oValue  : TOmniValue;
begin

  oValue := TOmniValue.Null;
//  TThread.Queue( nil, procedure
  TThread.Synchronize( nil, procedure
    begin
      //oValue := AFuncToCall( AData);
      oValue := TOmniValue.Create(['dhfljhadskjfasdkjfh']);
    end);

  Result := oValue;

end;
      repeat
        try
          if not ProcessMessage(Msg) then
            WaitMessage;
        except
          on e: Exception do
            writeln(e.Message);
        end;
      until Terminated

 

function TServerApp.ProcessMessage(var Msg: TMsg): Boolean;
var
  Handled: Boolean;
begin
  Result := False;
  //if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin
  if PeekMessage(Msg, NativeUInt(-1), 0, 0, PM_REMOVE) then begin
    Result := True;

    if Msg.Message <> WM_QUIT then begin
      Handled := False;

      case Msg.Message of
        sms_Processing             : ;
        sms_Quit                   : FCloseTimer.Enabled := true;
        sms_Synchronize            : SyncExecute;
        sms_Workerthread           : CreateWorkItem;
      end; // case

      //if Assigned(FOnMessage) then
      //  FOnMessage(Msg, Handled);
      if not Handled then begin
        TranslateMessage(Msg);
        DispatchMessage(Msg);
      end;
    end else begin
      FTerminate := True;
    end;
  end;
end;

 

Share this post


Link to post
23 minutes ago, t2000 said:

WaitMessage

 

What does that do?

Share this post


Link to post
Posted (edited)

For a quick test replace that with `CheckSynchronize(10)`.

 

Optimal would be a MsgWaitForMultipleObjects waiting on Thread or Process Termination and the SyncEvent while handling messages.

Edited by FredS

Share this post


Link to post
1 hour ago, FredS said:

Optimal would be a MsgWaitForMultipleObjects waiting on Thread or Process Termination and the SyncEvent while handling messages. 

Ok, that was my first approach. (see 1st post)

Only that I have to use TEvent and MsgWaitFor... instead of TWaitFor

Share this post


Link to post
1 hour ago, t2000 said:

see 1st post

 

I did, but I don't actually know what TWaitFor does..

I can tell you that the code you posted has a chance of being stuck not synchronizing.

Share this post


Link to post

For Synchronize mechanism to work, console application should be calling function CheckSynchronize from System.Classes repeatedly. You can do that from the message processing loop.

Share this post


Link to post

The reason is, I have to use a hardware with a DLL/TLB/ActiveX (I cannot use DLL directly) for getting a code on every receipt, that I create. And this works only one by one, but I want to use this with my REST Microservice.

Thank you all

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×