Programming C++Builder How to use the main VCL/CLX thread and thread-local variables?

How to use the main VCL/CLX thread?

The Execute method is your thread function. You can think of it as a program that is launched by your application, except that it shares the same process space. Writing the thread function is a little trickier than writing a separate program because you must make sure that you don't overwrite memory that is used by other threads in your application. On the other hand, because the thread shares the same process space with other threads, you can use the shared memory to communicate between threads.

When implementing the Execute method, you can manage these issues by

Using the main VCL/CLX thread

When you use objects from the VCL or CLX object hierarchies, their properties and methods are not guaranteed to be thread-safe. That is, accessing properties or executing methods may perform some actions that use memory which is not protected from the actions of other threads. Because of this, a main thread is set aside to access VCL and CLX objects. This is the thread that handles all Windows messages received by components in your application.

If all objects access their properties and execute their methods within this single thread, you need not worry about your objects interfering with each other. To use the main thread, create a separate routine that performs the required actions. Call this separate routine from within your thread's Synchronize method. For example:

void __fastcallTMyThread::PushTheButton(void)
{
  Button1->Click();
}

void __fastcallTMyThread::Execute()
{
  ...
  Synchronize((TThreadMethod)PushTheButton);
  ...
}

Synchronize waits for the main thread to enter the message loop and then executes the passed method.

Note: Because Synchronize uses the message loop, it does not work in console applications. You must use other mechanisms, such as critical sections, to protect access to VCL or CLX objects in console applications.

  1. You do not always need to use the main thread. Some objects are thread-aware. Omitting the use of the Synchronize method when you know an object't methods are thread-safe will improve performance because you don't need to wait for the VCL or CLX thread to enter its message loop. You do not need to use the Synchronize method for the following objects:
    Data access components are thread-safe as follows: For BDE-enabled datasets, each thread must have its own database session component. The one exception to this is when you are using Microsoft Access drivers, which are built using a Microsoft library that is not thread-safe. For dbDirect, as long as the vendor client library is thread-safe, the dbDirect components will be thread-safe. ADO and InterbaseExpress components are thread-safe.
    When using data access components, you must still wrap all calls that involve data-aware controls in the Synchronize method. Thus, for example, you need to synchronize calls that link a data control to a dataset by setting the DataSet property of the data source object, but you don't need to synchronize to access the data in a field of the dataset.
    For more information about using database sessions with threads in BDE-enabled applications, see Managing multiple sessions.
  2. DataCLX objects are thread-safe although VisualCLX objects are not.
  3. Graphics objects are thread-safe. You do not need to use the main VCL or CLX thread to access TFont, TPen, TBrush, TBitmap, TMetafile (VCL only), TDrawing (CLX only),or TIcon. Canvas objects can be used outside the Synchronize method by locking them.
  4. While list objects are not thread-safe, you can use a thread-safe version, TThreadList, instead of TList.

Call the CheckSynchronize routine periodically within the main thread of your application so that background threads can synchronize their execution with the main thread. The best place to call CheckSynchronize is when the application is idle (for example, from an OnIdle event handler). This ensures that it is safe to make method calls in the background thread.

How to use thread-local variables?

The thread function and any of the routines it calls have their own local variables, just like any other C++ routines. These routines also can access any global variables. In fact, global variables provide a powerful mechanism for communicating between threads.

Sometimes, however, you may want to use variables that are global to all the routines running in your thread, but not shared with other instances of the same thread class. You can do this by declaring thread-local variables. Make a variable thread-local by adding the __thread modifier to the variable declaration. For example,

int __thread x;

declares an integer type variable that is private to each thread in the application, but global within each thread.

The __thread modifier can only be used for global (file-scope) and static variables. Pointer and Function variables can't be thread variables. Types that use copy-on-write semantics, such as AnsiStrings don't work as thread variables either. A program element that requires runtime initialization or runtime finalization cannot be declared to be a __thread type.

The following declarations require runtime initialization and are therefore illegal.

int f( );
int __thread x = f( );   // illegal

Instantiation of a class with a user-defined constructor or destructor requires runtime initialization and is therefore illegal:

class X  {
   X( );
   ~X( );
};
X __thread myclass;   // illegal


Add comment


Security code
Refresh

Programming - C++Builder