web123456

Simple application of multi-threading in delphi



The program successfully avoids the conflict and it seems really simple that we succeeded! Of course, we can also use other synchronization techniques such as Mutex, Semaphore, etc., which are provided to us directly by Windows through the API.

The following summarizes several thread synchronization techniques commonly used in Windows.
1. Critical Sections (Critical Sections), if there are parts of the source code that can not be executed by two or more threads at the same time, you can use the critical sections to make this part of the code execution serialized. It can only be used in an independent process or an independent application program. The usage is as follows:
//In form creation
InitializeCriticalSection(Critical1)
//in form destruction
DeleteCriticalSection(Critical1)
// In the thread
EnterCriticalSection(Critical1)
...... protected code
LeaveCriticalSection(Critical1)
2. Mutex (mutual exclusion object), a global object used to serialize access to resources. We first set the mutex, then access the resource, and finally release the mutex. While setting a mutex, if another thread (or process) tries 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 usage is as follows:
//In form creation
hMutex:=CreateMutex(nil,false,nil)
//in form destruction
CloseHandle(hMutex)
// In the thread
WaitForSingleObject(hMutex,INFINITE)
...... protected code
ReleaseMutex(hMutex)
3. Semaphore, which is similar to a mutex, but it can be counted. For example, it allows a given resource to be accessed by three threads at the same time. In fact, a Mutex is a Semaphore with a maximum count of one, which is used as follows:
//In form creation
hSemaphore:= CreateSemaphore(nil,lInitialCount,lMaximumCount,lpName)
//in form destruction
CloseHandle(hSemaphore)
// In the thread
WaitForSingleObject(hSemaphore,INFINITE)
...... protected code
ReleaseSemaphore(hSemaphore, lReleaseCount, lpPreviousCount)
4. You can also use Delphi's TcriticalSection VCL object, which is defined in.

When you develop multithreaded applications and multiple threads access a shared resource or data at the same time, you need to think about thread synchronization.

Some ways to synchronize multiple threads in delphi
[ 2006-01-09 10:48:03 | Author: snox Font Size:Large |Medium |Small ]
When there are multiple threads, it is often necessary to de-synchronize these threads to access the same data or resource. For example, suppose there is a program in which one thread is used to read a file into memory, while another thread is used to count the number of characters in the file. Of course, it makes no sense to count the entire file until it is called into memory. However, since each operation has its own thread, the operating system treats the two threads as if they were separate tasks that are not related to each other, which makes it possible to count the number of characters without loading the entire file into memory. To solve this problem, you must make the two threads work synchronously.
There are a number of thread synchronization address problems, and Win32 provides a number of ways to synchronize threads. In this section you will see the use of critical areas, mutexes, semaphores, and events to address thread synchronization.
1. Critical areas
Critical areas are one of the most straightforward ways to synchronize threads. A critical zone is a piece of code that can only be executed by one thread at a time. If the code that initializes the array is placed in the critical area, another thread will not be executed until the first thread has finished processing it.
Before a critical section can be used, it must be initialized using the InitializeCriticalSection() procedure.
Its statement reads as follows:
procedure InitializeCriticalSection(var
The lpCriticalSection parameter is a record of type TRTLCriticalSection and is a variable parameter. It doesn't matter how the TRTLCriticalSection is defined, because it is rarely necessary to look at the specifics of what is in this record. Simply pass the uninitialized record in lpCriticalSection and the InitializeCriticalSection() procedure will populate this record.
Note that Microsoft deliberately hides the details of TRTLCriticalSection. This is because, its contents are different on different hardware platforms. On Intel-based platforms, TRTLCriticalSection contains a counter, a field indicating the current thread handle, and a handle to a system event. On Alpha platforms, the counter is replaced with an Alpha-CPU data structure called a spinlock. after the record is populated, we can start creating the critical section. At this point we need to encapsulate the code block with EnterCriticalSection() and LeaveCriticalSection(). These two procedures are declared as follows:
procedure EnterCriticalSection(var lpCriticalSection:TRRLCriticalSection);stdcall;
procedure LeaveCriticalSection(var
As you might expect, the parameter lpCriticalSection is the record populated by InitializeCriticalSection().
The DeleteCriticalSection() procedure should be called when you don't need the TRTLCriticalSection record, and here is its declaration:
procedure DeleteCriticalSection(var

2. Mutual exclusivity
Mutual exclusions are very similar to critical zones, except for two key differences: first, mutual exclusions can be used to synchronize threads across processes. Second, mutexes can be given a string name and additional handles to existing mutex objects are created by referencing this name.
Tip The biggest difference between a critical zone and an event object (such as a mutex object) is in performance. Critical zones take 1 0 ~ 1 5 time slices when there are no thread conflicts, while event objects take 400 ~ 600 time slices because they involve the system kernel.
The function CreateMutex ( ) can be called to create a mutex. Here is the declaration of the function:
function
The lpMutexAttributes parameter is a pointer to the TSecurityAttributtes record. This parameter is normally set to 0 to indicate the default security attributes. bThe bInitalOwner parameter indicates whether the thread that created the mutex is to be the owner of the mutex. When this parameter is False, the mutex has no owner.
The lpName parameter specifies the name of the mutex object. Set to nil means no name, if the parameter is not set to nil, the function searches to see if a mutex object with the same name exists. If so, the function returns a handle to the mutex with the same name. Otherwise, it creates a new mutex and returns its handle.
When finished using a mutex object, CloseHandle() should be called to close it.
Use WaitForSingleObject() in your program to prevent other threads from entering the synchronized region of the code. This function is declared as follows:
function

This function puts the current thread to sleep for the time specified by dwMilliseconds until the object specified by the hHandle parameter enters the signaling state. A mutually exclusive object enters the signaling state when it is no longer owned by the thread. A process enters the signaling state when it is about to terminate. dwMilliseconds can be set to 0, which means that only the object specified by the hHandle parameter is checked to see if it is in the signaling state, and then it returns immediately. dwMilliseconds is set to INFINITE, which means that it will wait if the signaling does not occur.
The return value of this function is as follows
Return value used by the WaitForSingleObject() function
Return Value Meaning
WAIT_ABANDONED The specified object is a mutex and the thread that owns the mutex has terminated without releasing the object. At this point the mutex is said to be abandoned. In this case, the mutex object is owned by the current thread and is set to the unsignaled state
WAIT_OBJECT_0 The specified object is in the signaling state.
WAIT_TIMEOUT waiting time has passed, the object is still non-signaling state again, when a mutex object is no longer owned by a thread, it is in the signaling state. At this time, the first call WaitForSingleObject () function of the thread becomes the owner of the mutex object, the mutex object is set to non-signaling state. When the thread calls the ReleaseMutex() function and passes a handle to the mutex as a parameter, this ownership relationship is lifted and the mutex re-enters the signaling state.
Note that in addition to the WaitForSingleObject() function, you can also use the WaitForMultipleObject() and MsgWaitForMultipleObject() functions, which wait for several objects to change to the signaling state. See the Win32 API online documentation for details on these two functions.
3. Signal volume
Another technique for synchronizing threads is to use a semaphore object. It is built on top of mutual exclusion, but the semaphore adds resource counting, where a predetermined number of threads are allowed to enter the code to be synchronized at the same time. A semaphore object can be created using CreateSemaphore() with the following declaration:
function
Like the CreateMutex() function, the first argument to CreateSemaphore() is a pointer to the TSecurityAttribute s record, which can be set to nil by default.
The lInitialCount parameter is used to specify the initial count value of a semaphore, which must be between 0 and lMaximumCount. A value greater than 0 indicates that the semaphore is in the signaling state. When the WaitForSingleObject() function (or any other function) is called, the count is reduced by one, and when ReleaseSemaphore() is called, the count is increased by one.
The parameter lMaximumCount specifies the maximum value of the count. If this semaphore represents a resource, then this value represents the total number of available resources.
The lpName parameter is used to give the name of the semaphore object, which is similar to the lpName parameter of the CreateMutex() function.
——————————————————————————————————————————
★★★★★★ About thread synchronization:
Synchronize() is run in a hidden window, where your main window will block off if you have a busy task; Synchronize() just puts that thread's code into the main thread and runs it, it's not thread synchronization.
Critical section is the best way to synchronize all the threads in a process, he is not system level, just process level, that is, he may use some flags within the process to ensure that the threads within the process are synchronized, according to Richter is a tally loop; Critical section can only be used within the same process; Critical section can only wait for an unlimited period of time, however, 2k added the TryEnterCriticalSection function to achieve 0 time to wait.
Mutual exclusion, on the other hand, ensures thread synchronization between multiple processes, and he uses the system kernel object to ensure synchronization. Since the system kernel object can be named, multiple processes can utilize this named kernel object to ensure thread security of system resources. Mutual exclusion is a Win32 kernel object, the operating system is responsible for the management; mutual exclusion can be achieved using WaitForSingleObject infinite wait, 0 time wait and arbitrary time wait.
1. Critical areas
Critical areas are one of the most straightforward ways to synchronize threads. A critical zone is a piece of code that can only be executed by one thread at a time. If the code to initialize an array is placed in a critical section, another thread will not be executed until the first thread has finished processing it. Before you can use the critical section, you must initialize it using the InitializeCriticalSection() procedure.
After the first thread has called EnterCriticalSection(), all other threads can no longer enter the code block. The next thread can't be woken up until the first thread calls LeaveCriticalSection().
2. Mutual exclusivity
Mutual exclusions are very similar to critical zones, except for two key differences: first, mutual exclusions can be used to synchronize threads across processes. Second, mutexes can be given a string name and additional handles to existing mutex objects are created by referencing this name.
Tip: The biggest difference between a critical zone and an event object (such as a mutex object) is in performance. Critical zones take 10 ~ 15 time slices when there are no thread conflicts, while event objects take 400 ~ 600 time slices because they involve the system kernel.
When a mutex is no longer owned by a thread, it is in a signaling state. The thread that first calls the WaitForSingleObject() function becomes the owner of the mutex, and the mutex is set to the unsignaled state. When the thread calls the ReleaseMutex() function and passes a handle to the mutex as a parameter, this ownership relationship is lifted and the mutex re-enters the signaling state.
A mutex can be created by calling the function CreateMutex(). When finished using the mutex object, CloseHandle() should be called to close it.
3. Signal volume
Another technique for synchronizing threads is to use a semaphore object. It is built on top of mutual exclusion, but the semaphore adds resource counting, where a predetermined number of threads are allowed to enter the code to be synchronized at the same time. A semaphore object can be created using CreateSemaphore().
Because only one thread is allowed into the code to be synchronized, the maximum count value (lMaximumCount) of the semaphore should be set to 1. The ReleaseSemaphore() function will add 1 to the count of the semaphore object;
Remember to always call the CloseHandle() function at the end to release the handle to the semaphore object created by CreateSemaphore().
★★★★★ The return value of the WaitForSingleObject function:
The object specified by WAIT_ABANDONED is a mutex and the thread that owns this mutex has terminated without releasing the object. At this point the mutex object is said to be abandoned. In this case, the mutex object is owned by the current thread, and it is set to the unsignaled state;
WAIT_OBJECT_0 The specified object is in the signaling state;
The time waited for WAIT_TIMEOUT has expired and the object is still in a non-signaling state;
——————————————————————————————————————————————
VCL supports three techniques to accomplish this:
(2) Use of critical areas
If the object does not improve the built-in locking features, you need to use the critical section, which is only accessible to maybe one thread at a time. In order to use the Critical section, an instance of the TCriticalSection global is generated. tcriticalSection has two methods, Acquire (which prevents other threads from executing the section) and Release (which unblocks it).
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 threads use it. When it's done, the thread calls the Release method to allow other threads to use this global memory as well by calling Acquire.
WARNING: The Critical zone is only accessible to global memory if all threads use it; if a thread calls the memory directly, without going through Acquire, it can cause simultaneous access problems. Example: LockXY is a global Critical zone variable. Any thread accessing the global X, Y variables must use Acquire before accessing them.
;{ lock out other threads }
try
Y := sin(X);
finally
;
end
Critical area is mainly for the realization of synchronization between threads, but the use of attention, be sure to use this critical object synchronized thread outside the establishment of the object (generally in the main thread to establish the critical object).
————————————————————————————————————————————————
Thread synchronization uses critical areas and process synchronization uses mutex objects.
Delphi encapsulates the critical object. The object is called TCriticalSection, the use of the main thread as long as the establishment of the critical object (note that the need to synchronize the thread must be outside the establishment of this object). Specific synchronization using Lock and Unlock can be.
While inter-process synchronization to create a mutex object, you only need to create a mutex object CreateMutex. When you need to synchronize, you only need to WaitForSingleObject(mutexhandle, INFINITE) and when you unlock, you only need to ReleaseMutex(mutexhandle); you can do it.
There are many ways to do this, semaphores, critical zones, mutexes, in addition to global atoms, shared memory and so on under windows. In windows, reading and writing an 8-bit integer is atomic, and you can rely on this for mutex methods. Methods that can generate global names can be used for inter-process synchronization (e.g., mutexes) or inter-thread synchronization; methods that cannot generate global names (e.g., critical zones) can only be used for inter-thread synchronization.