Update VCL from the same thread that created the user interface. What for?

I know that I have to call Synchronize to update vcl from a thread that did not create controls or send a message to the window.

I often heard that the word is not thread safe, but I cannot find an actual explanation of what is happening.

I know that an application can happen with access violation, but again I donโ€™t know why?

Please clarify this topic.

+8
multithreading thread-safety delphi
source share
3 answers

For security on GDI streams on Windows, see this help article .

It clearly states that you can safely access descriptors from multiple threads, but this should not be done at the same time. You need to protect access to GDI descriptors, for example. using critical sections.

Remember that GDI descriptors, like most Windows descriptors, are pointers to internal structures associated with integer ( NativeUInt on new Windows, for 64-bit compatibility). As always in multi-threaded computing, accessing the same content simultaneously can be a source of problems that are very difficult to identify and fix.

Part of the user interface of VCL itself should never have been thread safe, from the very beginning, as it relied on an error-free Windows API. For example, if you release a GDI object in a thread that is still needed in another thread, you will encounter a potential GPF.

Embarcadero (at this time) could make the VCL thread safe, serialize all user interface access through critical sections, but it can have additional complexity and reduce overall performance. Please note that even the Microsoft.Net platform (both in WinForms and WPF) also requires a dedicated thread to access the user interface, AFAIK.

So, to update the user interface from multiple threads, you have several templates:

  • Use Synchronize calls from the stream;
  • Send a special GDI message (see WM_USER ) from the background threads to notify the user interface thread that an update is required;
  • You have a stateless approach: the user interface will update its contents from time to time, from a logical level (using a timer or when you press some buttons that can change data).

From my point of view, I prefer option 2 for most user interfaces and option 3 (which can be mixed with option 2) for remote client-server access. Therefore, you do not need the server-side to trigger any update event for the user interface. In the HTTP / AJAX RESTful world, this really makes sense. Option 1 is somewhat slow, IMHO. In all cases, options 2 and 3 expect a clear n-tier multilevel architecture in which logic and interface do not mix: but this is a good template to follow in any case, for any serious development.

+8
source share

One of the biggest reasons there are no threads in VCL UI controls is the gett TWinControl.Handle property. This is not just easy read access to the HWND control. It also creates HWND if it does not exist yet. If a workflow reads the Handle property when no HWND exists, it creates a new HWND in the context of the workflow, which is bad because HWND tied to a thread creation context that displays control ownership almost does not work at best, because Windows messages for the control will no longer go through the main message loop. But worse, if the main thread reads the same Handle property at the same time as the workflow (for example, if the main thread dynamically recreates the Handle for any number of reasons), there is a race condition between which in the context of the thread creates an HWND that is assigned like the new Handle , as well as the potential for handle leakage if both threads create a new HWND , but only one can be saved and the other a leak.

Another intruder for a non-safe thread is the VCL function MakeObjectInstance() , which VCL uses internally to assign the non-static class method TWinControl.WndProc() as the message procedure in the TWinControl.Handle window, and also to assign any TWndMethod -typed object method as HWND message procedure created by the AllocateHWnd() function (used, for example, by TTimer ). MakeObjectInstance() does quite a lot of memory allocation / caching and smoothing the contents of this memory, which are not protected against simultaneous access by multiple threads.

If you can guarantee that the Handle control will be allocated in advance, and if you can ensure that the main thread never re-creates this Handle while the workflow is running, then you can safely send messages to this control from the workflow without using Synchronize() . But this is impractical, there are too many factors that a workflow must take into account. Therefore, it is best that all access to the user interface is performed only in the main thread. Thus, it is intended to use a VCL UI system.

+12
source share

Windows controls with handles are not thread safe (i.e., they cannot be safely accessed by two different threads at the same time), and Delphi wraps Windows controls to provide you with VCL controls. Since ARE controls access the main GUI thread, you need to leave them alone if you are running another thread.

+4
source share

All Articles