Delphi methods for multi-thread synchronization

Source: Internet
Author: User

Delphi methods for multi-thread synchronization

A thread is a relatively independent and schedulable execution unit in a process. An application can have a main thread, a main thread can have multiple sub-threads, and a sub-thread can also have its own

Sub-thread, which constitutes a multi-threaded application. Because multiple threads often access the same memory area at the same time, frequent accesses to this area will increase the probability of thread conflicts.

Once a conflict occurs, unpredictable results (the value of the public region is unpredictable) can be seen in the necessity of processing thread synchronization.
Note: AllCodeAll of them are described in Delphi. The debugging environment is Windows ME and Delphi 6. The involved Windows API functions can be obtained from msdn.

Detailed documentation.

First, we will reference an instance to introduce the following discussion. This instance does not take any measures to avoid thread conflicts. Its main process is: the main thread starts two threads

The global variables are read and written frequently, and the modified results are displayed in ListBox. Because the two threads are not synchronized, the threads generated an unexpected error when modifying letters.

Result.
The letters in each row in ListBox should be consistent, but the lines are different. This is the result of thread conflicts. When two threads simultaneously access the shared memory

The thread has not modified the memory, and the other thread has modified the memory. Because the write value process is not serialized, an invalid result is generated. Visible thread synchronization weight

Necessity.
The code in this example is as follows:
Unit. Pas File
Unit unit1;
Interface
Uses
Windows, messages, sysutils, variants, classes, graphics, controls, forms,
Dialogs, stdctrls;

// Define the window class
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 The ListBox component
Procedure execute; override;
Public
Lbox: tlistbox;
End;
// Define variables
VaR
Form1: tform1;
Letters: String = 'aaaaaaaaaaaaaaaaaaaaa'; // 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 // 1000 cycles increase the chance of conflict
If letters [J] <'Z' then
Letters [J]: = succ (letters [J])
Else
Letters [J]: = 'a ';
STR: = letters;
Synchronize (addtolist); // synchronously access the VCL visual component
End;
End;

Procedure tlistthread. addtolist;
Begin
Lbox. Items. Add (STR); // Add STR to the list box
End;

// Window class implementation
Procedure tform1.button1click (Sender: tobject );
VaR
Th1, Th1: tlistthread;
Begin
Listbox1.clear;
Listbox2.clear;
Th1: = tlistthread. Create (true); // create thread 1
2nd: = tlistthread. 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 multiple threads modify a public variable at the same time, there will be conflicts, so we should try to prevent it so that the multi-threaded application we developed can run stably.

. Next we will improve it. We first use the critical segments for serialization to achieve synchronization. Add the syncobjs unit to the uses section of the unit1.pas code in the previous example, and add the global critical segment variable (tr

Tlcriticalsection) critical1, add the initializecriticalsection (critical1) code to the formcreate event, and add dele to the formdestroy event.

Tecriticalsection (critical1) code, and then modify the tlistthread. Execute function. The modified code is as follows (? ):
Procedure tlistthread. Execute;
VaR
I, J, K: integer;
Begin
For I: = 0 to 50 do
Begin
? Entercriticalsection (critical1); // enter the critical section
For J: = 1 to 20 do
For K: = 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;
Okay, re-compile, as shown in (omitted)

ProgramIt seems very simple to avoid conflicts. We have succeeded! Of course, we can also use other synchronization technologies such as mutex (mutex object) and semaphore (semaphore)

These technologies are provided directly to us through APIs in windows.

The following summarizes several common thread synchronization technologies in windows.
1. critical sections (critical section ),Source codeIf there is a part that cannot be executed by two or more threads at the same time, you can use a critical segment to make this part of the code execution string

Line-based. It can only be used in an independent process or an independent application. The usage is as follows:
// Create a form
Initializecriticalsection (critical1)
// Destroy the form
Deletecriticalsection (critical1)
// Thread
Entercriticalsection (critical1)
...... Protected code
Leavecriticalsection (critical1)
2. mutex (mutex object) is a global object used for serializing access to resources. We first set the mutex object, then access the resource, and finally release the mutex object. Set mutex

If another thread (or process) tries to set the same mutex object, the thread will stop until the previous thread (or process) releases the mutex object. Note that it can

To be shared by different applications. The usage is as follows:
// Create a form
Hmutex: = createmutex (nil, false, nil)
// Destroy the form
Closehandle (hmutex)
// Thread
Waitforsingleobject (hmutex, infinite)
...... Protected code
Releasemutex (hmutex)
3. semaphore (semaphore), which is similar to a mutex, but can be counted. For example, a given resource can be simultaneously accessed by three threads. In fact, mutex is the most

Semaphore with a large count. The usage is as follows:
// Create a form
Hsemaphore: = createsemaphore (nil, linitialcount, lmaximumcount, lpname)
// Destroy the form
Closehandle (hsemaphore)
// Thread
Waitforsingleobject (hsemaphore, infinite)
...... Protected code
Releasesemaphore (hsemaphore, lreleasecount, lppreviouscount)
4. You can also use the VCL object tcriticalsection in Delphi, which is defined in syncobjs. Pas.

When developing multi-threaded applications and multiple threads simultaneously access a shared resource or data, you need to consider thread synchronization.

Methods for multi-thread synchronization in Delphi
[10:48:03 | Author: SnOx font size: large | medium | small]
When there are multiple threads, you often need to synchronize these threads to access the same data or resource. For example, assume that there is a program, one of which is used to read files to the memory,

The other thread is used to count the characters in the file. Of course, it makes no sense to count the entire file before it is transferred to the memory. However, since each operation has its own line

The operating system runs two threads as independent tasks, which may count the number of words when the entire file is not loaded into the memory. To solve this problem, you must

Threads.
There are some thread synchronization address problems. Win32 provides many thread synchronization methods. In this section, you will see how to use the critical section, mutex, semaphores, and events to solve thread synchronization.

Problem.
1. Critical Section
The critical section is the most direct thread synchronization method. A critical section is a piece of code that can only be executed by one thread at a time. If you place the code of the initialization array in the critical section

The other thread will not be executed until the first thread finishes processing.
Before using the critical section, you must use the initializecriticalsection () process to initialize it.
The statement is as follows:
Procedure initializecriticalsection (VAR
The lpcriticalsection parameter is a record of the trtlcriticalsection type and is a variable parameter. It doesn't matter how the trtlcriticalsection is defined,

Because you rarely need to view the specific content in this record. The initializecriticalsection () process will be filled by passing uninitialized records in the lpcriticalsection.

This record.

Note that Microsoft intentionally concealed the details of trtlcriticalsection. Because its content is different on different hardware platforms. On intel-based platforms, trtlcriticalse

Ction contains a counter, a domain indicating the current thread handle, and a system event handle. On the Alpha platform, the counter is replaced with an alpha-CPU

The data structure is called the spinlock. After the record is filled, we can create a critical section. In this case, we need to use entercriticalsection () and leavecriticalsectio

N () to encapsulate the code block. The two processes are declared as follows:
Procedure entercriticalsection (VAR lpcriticalsection: trrlcriticalsection); stdcall;
Procedure leavecriticalsection (VAR
As you may think, the parameter lpcriticalsection is the record filled by initializecriticalsection.
When you do not need a trtlcriticalsection record, you should call the deletecriticalsection () process. The following is its declaration:
Procedure deletecriticalsection (VAR

2. 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, the mutex can be assigned a string name and

Use this name to create additional handles for existing mutex objects.
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 1 0 ~ 1 5 time slices, and the event object

400 ~ 600 time slices.
You can call createmutex () to create a mutex. The Declaration of the function is as follows:
Function
The lpmutexattributes parameter is a pointer to a tsecurityattributtes record. This parameter is usually set to 0, indicating the default security attribute. Binitalowner Parameter Representation

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.
When a mutex object is used, closehandle () should be called to close it.
Use waitforsingleobject () in the program to prevent other threads from entering the code in the synchronization area. The function declaration is as follows:
Function

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. A mutex object is no longer

When a thread has a signal, it enters the sending status. When a process is terminated, it enters the sending status. The dwmilliseconds parameter can be set to 0, which means only the hhandle parameter is checked.

Specifies whether the object is in the sending status, and then returns immediately. The dwmilliseconds parameter is set to infinite, indicating that if the signal does not appear, it will keep waiting.
The return value of this function is as follows:
Return Value used by the waitfor singleobject () function
Meaning of Return Value
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. This

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 declared as a non-sending State. When a mutex object is no longer owned by a thread, it is in the sending State. In this case

First, the thread that calls the waitforsingleobject () function becomes the owner of the mutex object. The mutex object is set to not sending a signal. When the thread calls the releasemutex () function and transmits

When a mutex object handle is passed as a parameter, the ownership relationship is removed, and the mutex object enters the sending status again.
Note that in addition to the waitforsingleobject () function, you can also use the waitformultipleobject () and msgwaitformultipleobject () functions. They can wait for several

The object changes to the sending status. For details about the two functions, see the Win32 API online documentation.
3. semaphores
Another technique used to synchronize threads is to use semaphores. It is built on the basis of mutex, but the semaphore adds the resource count function, the number of threads allowed to enter at the same time

The code to be synchronized. You can use createsemaphore () to create a semaphore object. Its declaration is as follows:
Function
Like the createmutex () function, the first parameter of createsemaphore () is also a pointer to the tsecurityattribute s record. The default value of this parameter can be set

Nil.
The linitialcount parameter is used to specify the initial count value of a semaphore, which must be between 0 and lmaximumcount. If this parameter is greater than 0, the semaphore is in the signal state.

Status. When the waitforsingleobject () function (or another function) is called, the Count value is reduced by 1. When releasesemaphore () is called, this Count value is added to 1.
The lmaximumcount parameter specifies the maximum value of the Count value. If this semaphore represents a certain resource, then this value represents the total number of available resources.
The lpname parameter is used to give the name of the semaphore object. It is similar to the lpname parameter of the createmutex () function.
------------------------------------------
★★★About thread synchronization:
Synchronize () is run in a hidden window. If your task is busy here, your main window will be blocked; synchronize () is just to put the code of this thread

Running in the main thread, not thread synchronization.
The critical section is the best way to synchronize all threads in a process. It is not a system level, but a process level. That is to say, it may use some marks in the process to ensure the process.

According to Richter, It is a counting cycle; the critical section can only be used in the same process; the critical section can only wait for an indefinite period, but 2 k adds tryentercriticalsection

Function implementation 0 time wait.
Mutex ensures thread synchronization between multiple processes. It uses the system kernel object to ensure synchronization. Because the system kernel object can be named, multiple processes can exploit

This named Kernel Object ensures the thread security of system resources. The mutex is a Win32 kernel object, which is managed by the operating system. The mutex can be implemented using waitforsingleobject.

Unlimited waiting, zero waiting and any waiting time.
1. Critical Section
The critical section is the most direct thread synchronization method. A critical section is a piece of code that can only be executed by one thread at a time. If you place the code of the initialization array in the critical section

The other thread will not be executed until the first thread finishes processing. Before using the critical section, you must use the initializecriticalsection () process to initialize it.

After the first thread calls entercriticalsection (), all other threads cannot enter the code block. The next thread must wait for the first thread to call leavecriticalsectio

N () before being awakened.
2. 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, the mutex can be assigned a string name and

Use this name to create additional handles for existing mutex objects.
Tip: 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

400 ~ 600 time slices.
When a mutex object is no longer owned by a thread, it is in the sending State. In this case, the thread that calls the waitforsingleobject () function becomes the owner of the mutex object.

The mutex is set to not sending signals. When the thread calls the releasemutex () function and passes a mutex object handle as a parameter, the ownership relationship is removed and the mutex

The image enters the sending status again.
You can call createmutex () to create a mutex. When a mutex object is used, closehandle () should be called to close it.
3. semaphores
Another technique used to synchronize threads is to use semaphores. It is built on the basis of mutex, but the semaphore adds the resource count function, the number of threads allowed to enter at the same time

The code to be synchronized. You can use createsemaphore () to create a semaphore object,
Because only one thread is allowed to enter the code to be synchronized, the maximum count (lmaximumcount) of the semaphore must be set to 1. The releasesemaphore () function causes

Count Plus 1;
Remember, you must call the closehandle () function to release the handle of the semaphore object created by createsemaphore.
★★★Return Value of the waitforsingleobject 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. This

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;
----------------------------------------------
VCL supports three technologies to achieve this goal:
(2) Use the critical area
If the object does not improve the built-in locking function, you need to use the critical area. The critical area may only enter one thread at the same time. To use the critical zone, generate

Tcriticalsection global instances. The tcriticalsection has two methods: acquire (to prevent other threads from executing the region) and release (to cancel blocking)
Each critical zone is associated with the global memory you want to protect. Each thread accessing the global memory must first use acquire to ensure that no other thread uses it. Complete

The thread calls the release method so that other threads can use this global memory by calling acquire.
Warning: The critical zone only uses it in all threads to access the global memory. If a thread calls the memory directly without passing acquire, it will cause simultaneous access.

For example, lockxy is a global critical partition variable. Any thread that accesses global X and Y variables must use acquire before access.
Lockxy. acquire; {lock out other threads}
Try
Y: = sin (X );
Finally
Lockxy. release;
End
The critical section is mainly used to implement synchronization between threads. However, when using this section, you must create this object outside the thread where the critical object is synchronized.

Object ).
------------------------------------------------
The thread uses the critical section for synchronization, and the process uses mutex for synchronization.
Delphi encapsulates critical objects. The object name is tcriticalsection. When used, you only need to create this critical object in the main thread (note that the thread must be synchronized ).

Create this object ). Use lock and unlock for specific synchronization.
When a mutex object is created during synchronization between processes, you only need to create a mutex object createmutex. When you need to synchronize, you only need to waitforsingleobject (mutexhandle,

Infinite) Only releasemutex (mutexhandle); is required for unlock.
There are many methods, such as signal lights, critical sections, and mutex objects. In addition, global atoms and shared memory can be used in windows. In Windows, when an 8-digit integer is read and written, the original

Sub-, you can use this to achieve mutual exclusion. methods that can generate global names can be synchronized between processes (such as mutex objects), or used for inter-thread synchronization.

A method (such as a critical section) that can generate a global name can only be used for synchronization between threads.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.