I started to study the most important multi-threaded Global Data Reading and Writing. Combined with the example in the book, I changed it to the following situation:
Unit tst_thread3u;
Interface
Uses
Windows, messages, sysutils, variants, classes, graphics, controls, forms, dialogs, stdctrls;
Type
Tform1 = Class (tform)
Button1: tbutton;
Memo1: tmemo;
Button2: tbutton;
Button3: tbutton;
Procedure button1click (Sender: tobject );
Procedure button2click (Sender: tobject );
Procedure button3click (Sender: tobject );
Private
Procedure threadsdone (Sender: tobject );
End;
Tmythread = Class (tthread)
Protected
Procedure execute; override;
End;
VaR
Form1: tform1;
Implementation
{$ R *. DFM}
Const
Maxsize = 128;
VaR
Nextnumber: integer = 0;
Doneflags: integer = 0;
Globalarry: array [1 .. maxsize] of integer;
Lock: byte; // 1-not synchronized 2-critical section 3-mutex
CS: trtlcriticalsection; // critical section
Hmutex: thandle; // mutex
Function getnextnumber: integer;
Begin
Result: = nextnumber;
INC (nextnumber );
End;
Procedure tmythread. Execute;
VaR
I: integer;
Begin
Freeonterminate: = true; // automatically free after termination
Onterminate: = form1.threadsdone;
If lock <> 3 then // non-mutex
Begin
If lock = 2 then entercriticalsection (CS); // create a critical section
For I: = 1 to maxsize do
Begin
Globalarry [I]: = getnextnumber;
Sleep (5 );
End;
If lock = 2 then leavecriticalsection (CS); // exit the critical section
End else // ------- mutex
Begin
If waitforsingleobject (hmutex, infinite) = wait_object_0 then
Begin
For I: = 1 to maxsize do
Begin
Globalarry [I]: = getnextnumber;
Sleep (5 );
End;
End;
Releasemutex (hmutex); // release
End;
End;
Procedure tform1.threadsdone (Sender: tobject );
VaR
I: integer;
Begin
INC (doneflags );
If doneflags = 2 then
Begin
For I: = 1 to maxsize do
Memo1.lines. Add (inttostr (globalarry [I]);
If lock = 2 then deletecriticalsection (CS); // Delete the critical section
If lock = 3 then closehandle (hmutex); // disable mutex
End;
End;
// Non-synchronous
Procedure tform1.button1click (Sender: tobject );
Begin
Lock: = 1;
Tmythread. Create (false );
Tmythread. Create (false );
End;
// Critical section
Procedure tform1.button2click (Sender: tobject );
Begin
Lock: = 2;
Initializecriticalsection (CS); // initialize the critical section
Tmythread. Create (false );
Tmythread. Create (false );
End;
// Mutex
Procedure tform1.button3click (Sender: tobject );
Begin
Lock: = 3; // mutually exclusive
Hmutex: = createmutex (0, false, nil );
Tmythread. Create (false );
Tmythread. Create (false );
End;
End.
Without the help of the critical section and mutex, both threads are constantly output in memo1, and numbers are messy.
I. Critical Section
A critical section is a piece of code that can only be executed by one thread at a time. If the code of the initialization array is placed in the critical section, the other thread will not be executed until the first thread finishes processing.
Steps for using the critical section:
1. declare that the type of a global variable is trtlcriticalsection;
2. Call the initializecriticalsection () process before the thread create () to initialize the function. The function is defined as follows:
Void winapi initializecriticalsection (maid section );
The type of lpcriticalsection is the trtlcriticalsection encapsulated by Delphi.
3. Before the code of the thread that needs to be placed in the critical section, use the entercriticalsection (lpcriticalsection) process to establish the critical section. After the code is complete, use leavecriticalsection to mark the end of the critical section.
4. After the thread is executed, use lpcecriticalsection (lpcriticalsection) to clear the critical section. The cleanup process must be placed at the end of the thread execution, such as the formdesroy event. In the preceding example, an error occurs if the process is placed in tmythread. Create (false.
Ii. Mutual Exclusion:
Mutex is very similar to a critical section. Apart from two key differences: first, mutex can be used for cross-process thread synchronization. Second, mutex can be assigned a string name, and an additional handle of an existing mutex object is created by referencing this name.
The biggest difference between a critical section and an event object (such as a mutex object) is performance. When there is no thread conflict in the critical section, use 10 ~ 15 time slices, and the event object must use 400 ~ 600 time slices.
To use mutex:
1. Declare a global variable of the type thandle or hwnd, which is actually cardinal. Hwnd is the handle of window, which is mainly used for the window handle, while thandle has no restrictions.
2. Use createmutex () to create a mutex before the thread create. This function is defined:
Handle winapi createmutex (
Lpsecurity_attributes lpmutexattributes,
Bool binitialowner,
Lptstr lpname: pchar );
The lpsecurity_attributes parameter is a pointer to a tsecurityattributtes record. This parameter is set to nil, indicating the Default Security Attribute of the access control list.
The binitalowner parameter indicates whether the thread that creates the mutex object must be the owner of the mutex object. If this parameter is set to false, the mutex object has no owner.
The lpname parameter specifies the name of the mutex. If the parameter is not set to nil, the function searches for whether a mutex object with the same name exists. If yes, the function returns the handle of the mutex object with the same name. Otherwise, a new mutex object is created and its handle is returned.
The returned value is a handle. If an error occurs, null is returned. You can use the getlasterror function to view the error information.
Createmutex () can be used to prevent multiple instances from running. For example:
Program ABC;
Uses forms, windows ,...;
{$ R *. Res}
VaR
Hmutex: hwnd;
Begin
Application. initialize;
Hmutex: = createmutex (nil, false, pchar (application. Title ));
If getlasterror <> error_already_exists then
Begin
// The Doon to run the project
End;
Releasemutex (hmutex );
Application. Run;
End;
In this section, we only want to prevent threads from entering the synchronization code area, so the lpname parameter is set to nil.
3. Use the waitforsingleobject () function before synchronizing code. This function enables the thread to obtain the ownership of mutex objects (Synchronous Code. This function is defined:
DWORD winapi waitforsingleobject (
Handle hhandle,
DWORD dwmilliseconds );
This function allows the current thread to sleep at the time specified by dwmilliseconds until the object specified by the hhandle parameter enters the signal sending State. When a mutex object is no longer owned by a thread, it enters the signal sending State. When a process is terminated, it enters the sending status. The dwmilliseconds parameter can be set to 0, which means to check whether the object specified by the hhandle parameter is in the sending status and then return immediately. The dwmilliseconds parameter is set to infinite, indicating that if the signal does not appear, it will keep waiting.
Meaning of the return value of this function:
The wait_abandoned specified object is a mutex object, and the thread that owns this mutex object is terminated before the object is released. In this case, the mutex object is discarded. In this case, the mutex object belongs to the current thread and is set to a non-sending State.
Wait_object_0: the specified object is in the sending status.
The wait_timeout wait time has passed, and the object is still in the non-sending status.
Again, when a mutex object is no longer owned by a thread, it is in the sending status. In this case, the thread that calls the waitforsingleobject () function becomes the owner of the mutex object, and the mutex object is set to not sending a signal. When the thread calls the releasemutex () function and passes a mutex object handle as a parameter, this ownership relationship is removed and the mutex object enters the signal sending State again.
Note that in addition to the waitforsingleobject () function, you can also use the waitformultipleobject () and msgwaitformultipleobject () functions. They can wait for several objects to change to the sending status. For details about the two functions, see the Win32 API online documentation.
4. Use the releasemutex (thandle) function to mark the synchronization code. This function only releases the relationship between the mutex object and the thread owner, and does not release the handle of the mutex object.
5. Call closehandle (thandle) to close the mutex object. Note the position of the function in the routine.