ChrisChuah 0 Posted February 28, 2023 Hi I have a component like this TMyComponentEvent = procedure (Sender : TObject ; aData : string) of object; TMyComponent = class(TComponent) private FClient : TidTCPClient; FProcessList : TmyStringList; FReadThreadComponent : TidThreadComponent; FProcessThreadComponent : TidThreadComponent; FOnMyEvent : TMyComponentEvent; procedure DoMyEvent(aData : string); dynamic; ... ... procedure ReadThreadComponentRun(Sender : TidThreadComponent) procedure ProcessThreadComponentRun(Sender : TidThreadComponent) .. .. published property OnMyEvent : TMyComponentEvent read FOnMyEvent write FOnMyEvent; end; procedure register; begin RegisterComponents('MyComponents', [TMyComponent]); end; // cc : the UI screen will have this component and will have a set of code // cc : to implement on this event procedure TMyComponent.DoMyEvent(aData : string); begin if assigned(FOnMyEvent) then FOnMyEvent(self, aData); end; // cc : assume that the TidTCPCLient is connected to the server // cc : so the FReadThreadComponent will always get the data from TidTCPClient procedure TMyComponent.ReadThreadComponentRun(Sender :TidThreadComponent) var l_readBytes : TIdBytes; l_processdata : string; begin FClient.IOhandler.ReadBytes(l_ReadBytes, -1, false); if length(l_readBytes) = 0 then exit; {There are some headers to remove from the l_readBytes l_processData := ProcessidBytes(l_readBytes); FProcessList.add(l_processData); end; // cc : this is another idThreadComponent that will take the first item from the myStringList // cc : and process it procedure TMyComponent.ProcessThreadComponentRun(Sender : TidThreadComponent); var l_data : string; begin // cc : take the first item from the MyStringList l_data := FProcessList.getFirstData; if (l_data[1] = '1') then DoMyEvent(l_data); end; In the main form TMainForm.MyComponentMyEvent(Sender : TObject; aData : string) begin TThread.Synchronize (nil, procedure begin // update the FMX Form UI button, grid etc. end); end; Now, the question is, if i DO NOT use the TThread.Synchronize, there will be problem updating the UI grid etc. What is the difference if i use the TThread. Synchronize and TThread.Queue Somehow, if i use the Synchronize function, it is slower in updating the UI than TThread.Queue. How can i make the UI updating more responsive? Am i doing it correctly or can anyone advise? Also there is some hanging issue whereby the function using the TThread.Queue can be called by Clipboard event. Maybe i will need to capture that event screen again Share this post Link to post
Remy Lebeau 1436 Posted February 28, 2023 13 hours ago, ChrisChuah said: Now, the question is, if i DO NOT use the TThread.Synchronize, there will be problem updating the UI grid etc. Correct, because your event is being fired in a worker thread, so any access to UI controls must be synchronized with the main UI thread. 13 hours ago, ChrisChuah said: What is the difference if i use the TThread. Synchronize and TThread.Queue TThread.Synchronize() runs synchronously. It will not return to the caller until the synched method has been called and exited. That means your worker thread will be blocked waiting on the main UI thread to call the method. TThread.Queue() runs asynchronously. It will return to the caller immediately, and the synched method will run in the background at some unspecified time when the main UI thread gets around to calling it. That means your worker thread will not be blocked waiting on the main UI thread, it will be free to do other things while the synched method waits to be called. 13 hours ago, ChrisChuah said: Somehow, if i use the Synchronize function, it is slower in updating the UI than TThread.Queue. Yes, because your worker thread can't do anything while TThread.Synchronize() is blocked waiting. You don't have that issue with TThread.Queue(). 13 hours ago, ChrisChuah said: Also there is some hanging issue whereby the function using the TThread.Queue can be called by Clipboard event. What do you mean? Please elaborate. Share this post Link to post
ChrisChuah 0 Posted March 1, 2023 Hi remy Ignore my last statement on the TThread.Queue can be called by Clipboard event. I am still trying to figure out why my application UI can hang or irresponsive when there is a large amount of data is received from the TidTCPClient. The TidThreadComponent will be busy processing and send out events to the MyEvent function as shown below TMainForm.MyComponentMyEvent(Sender : TObject; aData : string) begin TThread.Synchronize (nil, procedure begin // update the FMX Form UI button, grid etc. end); end; My app is then reported by windows task manager that it is not responding. How can i check where the problem lies? Is it at the TThread.Synchronized in the MyComponentMyEvent? I cannot click on any part of the application UI anymore. This is an FMX application running in Windows 32 bit. Would I not face any of this hanging issue if i change to a VCL application instead of FMX? Please advise regards Chris Share this post Link to post
Lars Fosdal 1793 Posted March 1, 2023 I really REALLY dislike Synchronize. It is a pitfall of pitfalls. IMO, a better approach is: In your thread, send a signal when there is something that needs to be refreshed. That signal can be a queue, and you may want to include info about the type of content change if the UI should do a selective refresh Decide how often the UI should refresh and make a timer in the UI that checks the queue if something should be refreshed You still need to protect access to elements shared by the UI thread and the background threads - but at least you will have FULL control over what the UI draws and when. 1 Share this post Link to post
ChrisChuah 0 Posted March 1, 2023 I agree with you. It is really a headache when dealing with Thread and synchronisation. In Indy TCP component, i need to use the Thread component to read the TCP socket constantly for data. When there is data, need to append to a buffer or memstream as network data does not come in nice chunk of data with start and end. Then we need to process that memstream for the appropriate data and remove the part of the data process from the memstream and add to another queue or List. Then we need to create another thread to process this List to send out events. Need to create the thread so that it will hog up the first thread that read data from the TCP component. In this processing thread, need to fire event based on the message. In this event, if there is a need to update the Main form UI, we need to use the TThread.Synchronize or TThread.Queue. If I were to update the data in memory, then i would not need to use TThread isnt it? <== Am i on the correct path? So, if i update the data in memory and i use the timer to refresh the Main form UI, Do i still need to use TThread.Synchronize or TThread.Queue? But using System.TTimer to refresh the data would have a lower priority in refreshing as if there are lots of data coming in on the TCPClient, the updates would use up the CPU and the timer will be set back to later time when the CPU is less busy isnt it? Please advise Share this post Link to post
Lars Fosdal 1793 Posted March 1, 2023 2 minutes ago, ChrisChuah said: So, if i update the data in memory and i use the timer to refresh the Main form UI, Do i still need to use TThread.Synchronize or TThread.Queue? Not if you use a thread safe queue and the access to the data that needs to be updated are protected with f.x. a critical section, or you only access data that you know will not be touched by other threads. 2 minutes ago, ChrisChuah said: But using System.TTimer to refresh the data would have a lower priority in refreshing as if there are lots of data coming in on the TCPClient, the updates would use up the CPU and the timer will be set back to later time when the CPU is less busy isnt it? Lets say you have a 500ms timer. If you have activities on the TCPClient that receive data every 50 ms - the UI would still only refresh only every 500 ms The only time you would get an issue with performance in the UI, is if the render takes longer than the timer - but you would not lose any data - and assuming the render contains all the most recent data - it would all be displayed, albeit at your fixed timer frequency. In most cases, throttling the UI updates to refresh less often than the actual data refresh-rate will not be an issue. If your TCPClient thread consumes all your CPU - you have a very different problem. The only way it would be a problem, is if you use UI elements as data storage, instead of having an underlying structure as you should. Divide and conquer. Separate your UI from your business logic. 1 Share this post Link to post
Dalija Prasnikar 1404 Posted March 1, 2023 35 minutes ago, Lars Fosdal said: The only time you would get an issue with performance in the UI, is if the render takes longer than the timer This can also be easily solved if you either disable timer while you are updating UI and enable it back when you are finished. Alternative solution is adding some boolean flag and simply skipping UI update if previous update is still running. 1 Share this post Link to post
Remy Lebeau 1436 Posted March 1, 2023 9 hours ago, ChrisChuah said: I am still trying to figure out why my application UI can hang or irresponsive when there is a large amount of data is received from the TidTCPClient. If you are doing a lot of syncing with the main UI thread, you are likely overwhelming the main thread with too many requests, leaving it with less opportunity to handle UI messages. Every time you call TThread.Synchronize() or TThread.Queue(), it puts the synced method into a list and then posts a message to the main thread to let the RTL know that the list is pending. Once the main thread processes that message, it runs through the entire list, calling each method in order, and doesn't process new messages again until after the list is empty. If you are syncing often, you are going to fill the main thread's message queue with pending requests, and so the main thread is going to spend its time checking and rechecking that list instead of doing other things. So, try to reduce how often you sync with the main thread. For instance, batch up your synced data and then sync the whole batch after X updates are cached, or after N seconds have elapsed, etc. Or, save off just your latest data and use a UI timer in the main thread to grab that data periodically. 9 hours ago, ChrisChuah said: My app is then reported by windows task manager that it is not responding. That means the main UI thread is doing too much work and is not processing UI messages often enough. You need to relieve the pressure on the main thread. 9 hours ago, ChrisChuah said: This is an FMX application running in Windows 32 bit. Would I not face any of this hanging issue if i change to a VCL application instead of FMX? No, this would affect VCL in much the same way, as most of the functionality for TThread.Synchronize()/TThread.Queue() is common RTL code that is shared by both frameworks. Share this post Link to post