The thread is a relatively independent, scheduled execution unit in the process. An application can have a main thread, a main thread can have multiple sub-threads, and the sub-thread can have its own sub-thread, which constitutes a multi-threaded application. Since multiple threads tend to access the same block memory area, frequent access to this area will increase the probability of generating thread conflicts. Once a conflict is created, the unpredictable result will be made (the value of the common area is unpredictable) The need to process thread synchronization can be seen.
Note: All the code appeared in this article is described in Delphi, the debugging environment is Windows Me, Delphi 6. The Windows API functions involved can get a detailed document from MSDN.
Firstly, an example will lead to the discussion below, this instance does not take any measures to avoid thread conflicts, its main process is: starting the two threads by the main thread to perform frequently read and write the global variables of Letters, then modify The result is displayed in ListBox. Since there is no synchronization of these two threads, threads have generated unpredictable results when modifying Letters. Everyone can look at the results it run, as shown:
The letters of each line in ListBox should be consistent, but the drawing line is different, which is the result of thread conflicts. When two threads accesses the shared memory at the same time, a thread has not completed the memory, and the other thread modified the memory. Since the process of writing is not serialized, this has an invalid result. . Visible thread synchronization importance.
The following is the code of this example
Unit.pas file
Unit unit1;
Interface
Uses
Windows, Messages, Sysutils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, stdctrls;
// Define window classes
Type
TFORM1 = Class (TFORM)
Listbox1: tlistbox;
Listbox2: tlistbox;
Button1: tbutton;
Procedure Button1Click (Sender: TOBJECT);
Private
{Private Declarations}
public
{Public declarations}
END;
// Define the thread class
Type
TLISTTHREAD = Class (TTHREAD)
Private
Str: string;
protected
Procedure addtolist; // Add STR to listbox components
Procedure execute; override;
public
LBOX: TListBox;
END;
/ / Define variables
VAR
FORM1: TFORM1;
Letters: string = 'aaaaaaaaaaaaaaaaaaaa; // global variable
IMPLEMENTATION
{$ R * .dfm}
// Thread class implementation
Procedure tlistthread.execute;
VAR
I, J, K: Integer;
Begin
For i: = 0 to 50 do
Begin
For j: = 1 to 20 do
For K: = 1 To 1000 do // Cycle 1000 increases in the chance of conflicting
IF letters [j] <'Z' Then
Letters [J]: = SUCC (Letters [J])
Else
Letters [J]: = 'a';
Str: = letters;
Synchronize (addtolist); / / Synchronize access VCL visual components
END;
END;
Procedure tlistthread.addtolist;
Beginlbox.Items.Add (STR); // Add STR to the list box
END;
// Window Class Implementation
Procedure TFORM1.BUTTON1CLICK (Sender: TOBJECT);
VAR
TH1, TH2: TLISTTHREAD;
Begin
Listbox1.clear;
Listbox2.clear;
TH1: = TLISTHREAD.CREATE (TRUE); // Create thread 1
TH2: = TLISTHREAD.CREATE (TRUE); // Create thread 2
Th1.lbox: = ListBox1;
TH2.LBOX: = ListBox2;
TH1.Resume; // Start execution
TH2.Resume;
END;
End.
As can be seen from the above example, when a plurality of threads modify a public variable, a conflict occurs, so we have to try to prevent it, so that we develop multi-thread applications can run stable. Let's improve it. Let us use the critical paragraph to serialize and achieve synchronization. Was added to the uses section of the embodiment unit1.pas code SyncObjs means added global critical section variable (TRTLCriticalSection) Critical1, was added InitializeCriticalSection (Critical1) FormCreate this code, the event was added DeleteCriticalSection (Critical1) this code in the event FormDestroy , Then modify the TListThread.execute function, the modified code seems as shown below (► To add code):
Procedure tlistthread.execute;
VAR
I, J, K: Integer;
Begin
For i: = 0 to 50 do
Begin
►TercriticalSection (critical1); // enters the critical section
For j: = 1 to 20 do
Fork: = 1 to 3000 DO
IF letters [j] <'Z' Then
Letters [J]: = SUCC (Letters [J])
Else
Letters [J]: = 'a';
Str: = letters;
►LeaveCriticalSection (critical1); // Exit the critical section
Synchronize (addtolist);
END;
END;
Ok, recompile, the results are shown below:
The program successfully avoids conflict, it seems really simple, we have succeeded! Of course, we can also use other synchronization techniques such as Mutex (mutually exclusive objects), Semaphore, etc., these technologies are all direct to us through the API.
Here, summarize several thread synchronization techniques commonly used by Windows.
1. Critical Sections, if there is a portion that cannot be executed simultaneously by two or more threads, the code can be used to perform serialization with a critical section. It can only be used in a separate process or a separate application. The method is as follows: // InitializeCriticalsection (critical1) // in the form destroy DeleteCriticalSection (critical1) // in the thread EntercriticalSection (critical1) ... Protected code LeaveCriticalSection (critical1)
2. MUTEX (mutually exclusive) is a global object used to serialize access resources. We first set the mutually exclusive object, then access the resource, and finally release the mutual exclusive object. When setting a mutex, if another thread (or process) attempts to set the same mutex, the thread will stop until the previous thread (or process) releases the mutex. Note that it can be shared by different applications. The use method is as follows: // HMutex: = Createmutex (NIL, FALSE, NIL) in the form is destroyed in the form, WaitForsingleObject (hmutex, infinite) in the process ... Protection code ReleaseMutex (HMutex) 3. Semaphore, which is similar to the mutex, but it can count. For example, a given resource is allowed to be accessed by three threads simultaneously. In fact, Mutex is a Semaphore for maximum counts. The use method is as follows: // HSEMAPHORE: = CreateSemaphore (NIL, LinitialCount, Lmaximumumcount, LPNAME) // in the form destroy, WaitForsingleObject (HSEMaphore, Infinite) in the process ... Protection Code ReleaseSemaphore (HSemaphore, Lreesecount, LPPREVIOUSCOUNT)
4. You can also use the TCRITicalSECTION in Delphi, which is defined in Syncobjs.PAS.
When you develop multithreaded applications, and multiple threads accesses a shared resource or data, you need to consider the problem of thread synchronization.
If you want to get the electronic version of this article and its source code, please download it on the http://codehunter.1yes.net website.
2001
year
9
month
5
Day Wednesday