Use of Delphi thread class (1)

xiaoxiao2021-03-06  64

Delphi thread

Raptor [Mental Studio] (Personal Column) (Blog)

Http://eental.mentsu.com

At the end of last year, I wrote an article "Thread class, 2, 3, 4, 5" in Delphi (hereinafter referred to herein "), analyzed the implementation details of the TTHREAD class, analyzed the use of TTHRead class Note some problems. It is estimated that the article is too theoretical, and there is no combination of practical applications, so this article will be completed.

Derived thread class

Access visual control in multi-threaded applications

Thread data sharing

Inter-thread communication

Prevent dead lock

Normal thread end mode

Mandatory end thread

Application example: Multithreaded database application

Application example: Multi-threaded network application

Application example: Thread pool technology

Derived thread class

Before using the use of threads, you must first explain some aspects of the distribution thread class.

In general, the derived thread class is similar to the following:

TDEMO1THREAD = Class (TTHREAD)

Private

{Private Declarations}

/ / Some of the data used herein

Procedure syncproc; // Used to be called by synchronize

protected

Procedure execute; override;

public

Constructor create; // Constructor, initialization

END;

In the foregoing, threads, threads, and thread class objects are different things, as follows:

The thread thread is a code that can be executed concurrently, which is the implementation code of the Execute method in the Delphi thread class, including all code that is directly or indirectly called in this method implementation. Thread class package a class that is packaged, which includes thread method Execute, thread data (note, its data member is not partial data of the thread, to consider accessing conflicts on their access, and will be detailed later in this point) , And other methods (although it is a thread type method, most actually run in the main thread, seeing the thread class object, the thread class, which is basically the same as the general class. The corresponding data area is assigned, used to record thread data, because it is allocated in the heap space allocated in the process, is not partial data for the thread, so it is necessary to consider data access conflicts

There are two things here must be clear: one is a thread is a dynamic concept; the other is an instantiated detail of the class.

The so-called thread is a dynamic concept that it is meaningful to discuss the thread code in the case of execution, which is meaningful in rest. For example, in general, the code in the Execute method can be said to be a thread code, that is because it is in normal case, it is called by the actual thread code threadproc, is executed in the thread. . But this is only usually usually, and the extreme example: If the thread class's execute is placed in the public, then calls Execute in the main thread, then Execute is a thread code because it is called by the main thread, essentially It's up to say that it is the main thread code.

Conversely, all the code called directly or indirectly (, as in normal case) (but not through the synchronize or similar approach) is a thread code, such as a code other than the thread class other than the Execute. That's why accessing the VCL component in the thread must pass Synchronize, because otherwise, it is multithreading, which may cause data access conflicts. However, by Synchronize, you can pass the sub-thread to execute the code to the main thread. If the main thread is executed, this code is not a sub-thread code, but a general main thread code, so there is no problem. Examples of explaining questions:

TDEMO1THREAD = Class (TTHREAD)

Private

FDATA: Integer;

protected

Procedure execute; override;

public

PROCEDURE FOO;

END;

Procedure tdemo1thread.execute;

Begin

Foo;

Form1.caption: = 'Changed by thread'; // Error: VCL operations that are not thread secure

END;

PROCEDURE TDEMO1THREAD.FOO;

Begin

FDATA: = fdata 1;

END;

/ / Call in the main thread

Procedure TFORM1.BUTTON1CLICK (Sender: TOBJECT);

VAR

T: tdemo1thread;

Begin

T: = tdemo1thread.create (false);

T.foo; // Error: Foo operations data member fdata, and foo is called at the same time by Execute, there is access conflict

T.free;

END;

The process foo is called simultaneously by the Execute and the main thread, which is (sub) thread code when it is executed by the Execute, when it is executed by the main thread, is not executed. In Execute, modify the form header through the CAPTION property of Form1 to make TFORM's setcaption code, because VCL is not a thread, so this operation may result in unpredictable consequences. At the same time, due to the modification of the data member FDATA in the FOO, when the sub-thread and the main thread may call the FOO, it may result in data access conflicts. The specific analysis of access conflicts is seen as follows.

The following figure is an execution of a typical threaded application. Obviously, in the thread class code, only the Execute method is a thread code because it is called by true thread method ThreadProc, and the constructor Create of the thread class CREATE and SyncProc protected by Synchronize are the main thread code. But if SyncProc is called directly in Execute, it will also become a thread code.

Further, see the instantiation details of the class. Instantiation of thread classes is the same as that of the general class, but there are differences, the biggest difference is that it also creates a thread (ie, the execution of ThreadProc) while instantiating.

For the instantiation of the general class: first is the call class constructor CREATE, and Create's work is first assigned to the data area of ​​the class (including data members, including the class data), and some necessary additional data). Then, the pointer to this data area is then called CREATE to complete the constructor as the SELF parameter. Thereafter, the class instance we used is actually specific is this data area. For example, we want to call a member of a certain class instance (someMemethod) as follows: SomeObject.somemembermethod (parameters);

In fact, it is equivalent to:

TsomeClass.Somemembermethod (someObject, parameters);

The TsomeClass is the class corresponding to the class instance SomeObject, and the class instance SomeObject is passed to the method inside the method as an implicit parameter Self.

When accessing the class data member within the method:

Function TsomeClass.SomememberMethod (parameters): SomeType; // Because Self is implied in implied

Begin

Result: = Somememberdata;

END;

In fact, equivalent to Self.SomememberData.

For thread classes, it is basically the same as above.

But there is a biggest difference that the thread class instance creates its own stand-alone stack (hidden by thread function threadproc), and the normal class is the use of the main thread. This means that the use of local variables in the sub-thread is safe because local variables are allocated in the stack. Each thread has its own stack (including the main thread), and in general, it is not possible to directly access the stack space of other threads, unless some extreme cases (such as transmitting local variables to other threads for operation), There is no need to access conflict protection.

However, this does not represent thread class data members (such as FDATA), because they are allocated in the plug-in space. Of course, each thread class object has its own independent data member, and it is still safe if it is not accessible to each other. But if you need to let other threads, especially the main thread use thread class data, you must take into account the issue of access conflict protection (such as the previous example, the FDATA has led to its access to it by the FOO function), because this Conflict Access is usually indirect through the method / attribute of the threaded class itself, sometimes it is easy to neglect.

The implementation of the thread class is as follows:

Code area data area

In the derived thread class, the last thing to say is: often check the TERMINATED attribute. Because the normal thread end mode is to end the thread code returns (not returning the main thread, it is returned to the operating system). Therefore, if the operation of performing a long time in the thread needs to be interrupted at any time, the recommended approach is to divide a long time into a lot of short operations (time limits the delay in the interactive operation) within the acceptable range If you can accept it within a second second after pressing the cancel button, the short operation does not have longer than one second), then use the loop to perform these short operations, and check the TERMINATED attribute when each loop is cycled. To Terminated is true, you can cancel your operation immediately. Usually implemented with the following code:

Procedure tdemo1thread.execute;

Begin

While (Not Terminated) Dobegin

// short operation

END;

END;

转载请注明原文地址:https://www.9cbs.com/read-86569.html

New Post(0)