t2000 1 Posted July 8, 2020 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
Lars Fosdal 1793 Posted July 9, 2020 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
Primož Gabrijelčič 223 Posted July 9, 2020 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
Keesver 23 Posted July 9, 2020 I think this should be TThread.Synchronize because the code needs to wait for the result. Share this post Link to post
t2000 1 Posted July 9, 2020 @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 But I have to use: res := ProcessInMainThread(para); Share this post Link to post
Lars Fosdal 1793 Posted July 9, 2020 Use a worker thread which stays active and take tasks through a queue. Share this post Link to post
t2000 1 Posted July 9, 2020 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
Primož Gabrijelčič 223 Posted July 9, 2020 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
t2000 1 Posted July 9, 2020 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
FredS 138 Posted July 9, 2020 23 minutes ago, t2000 said: WaitMessage What does that do? Share this post Link to post
FredS 138 Posted July 9, 2020 (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 July 9, 2020 by FredS Share this post Link to post
t2000 1 Posted July 9, 2020 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
FredS 138 Posted July 9, 2020 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
Primož Gabrijelčič 223 Posted July 10, 2020 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
Lars Fosdal 1793 Posted July 10, 2020 IMO, minimizing or avoiding Synchronize is preferable. Share this post Link to post
t2000 1 Posted July 10, 2020 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