Threads in Delphi - (4)

xiaoxiao2021-03-06  34

Thread class in Delphi

Raptor [Mental Studio] http://ental.mentsu.com

CriticalSection is a technology that shares data access protection. It is actually equivalent to a global Boolean variable. However, it is different from it, it only has two operations: Enter and Leave, which can also use two states as True and False, indicating whether it is in the critical area, respectively. These two operations are also primitives, so it can be used to protect shared data in multi-threaded applications to prevent access violations. Method for protecting data with critical regions is simple: Calling Enter settings to enter the critical zone flag each time you have to access shared data, then operate the data, and finally call Leave to leave the critical area. Its protection principle is this: When a thread enters the critical area, if another thread is to access this data, it will find the thread to enter the critical area when calling Enter, and then this thread will be Hang up, wait for the thread calling at the critical area to call Leave leaves the critical area, when another thread is completed, this thread will be woken up, and set the critical area flag to start the data, which prevents access. conflict. To the front of the InterlockedIncrement example, we use CriticalSection (Windows API) to achieve it: Var InterlockedCrit: TRTLCriticalSection; Procedure InterlockedIncrement (var aValue: Integer); Begin EnterCriticalSection (InterlockedCrit); Inc (aValue); LeaveCriticalSection (InterlockedCrit); End; Now let's see the previous example: 1. Thread A enters the critical area (assuming data is 3) 2. Thread B enters the critical area, because A is already in the critical area, so B is hang 3. Thread a pair data plus data Now is 4) 4. Thread a Leave the critical area, wake up thread B (now the data in memory is 4) 5. Thread B is awakened, add one for data (now 5) 6. Thread B Leave the critical area, now The data is correct. The critical area is to protect access to sharing data. With regard to the use of the critical regions, it is important to pay attention to: ie the abnormal conditions of data access. Because if an exception occurs in the data operation, the Leave operation will result in not being executed, and the result will make the thread that should be woken out is not awakened, which may cause no response of the program. So, in general, as this is the correct approach: EntercriticalSECTION TRY // Operate the critical area Data Finally LeavecriticalSECTION END;

Finally, it will be described that Event and criticalSECTION are operating system resources. You need to be created before use. It also needs to be released after use. For example, a global Event: Syncevent and global criticalsection: Thetsynchronization and DonThreadsynchronization are created and released in the initThreadsynchronization and DonThreadsynchronization, while they are called in the initialization and finalization of the Classes unit. Since both the API is used in TTHREAD, Event and CriticalSecion are used, so that the API is an example, in fact, Delphi has provided packages for them, in the Syncobjs unit, are the TEVENT class and the TcriticalSECTION class. The usage is also different from the method of using the API in front. Because of the TEVENT's constructor parameters, Delphi provides an Event class initialized with the default parameters: TsIMpleEvent. By the way, introduce another class for thread synchronization: TMULTIREXCLUSIVEWRITESYNCHRONIZER, it is defined in the SYSUTILS unit. As far as I know, this is the longest class name defined in Delphi RTL, but it has a short alias: Tmrewsync. As for its use, I want to see the name, I can know, I will not say much. With the preparation knowledge of Event and CriticalSection, you can formally began to discuss Synchronize and WaitFor.

We know that SYNCHRONIZE is performed by putting some code in the main thread, because in a process, there is only one main thread. Let's look at the Synchronize implemented: procedure TThread.Synchronize (Method: TThreadMethod); begin FSynchronize.FThread: = Self; FSynchronize.FSynchronizeException: = nil; FSynchronize.FMethod: = Method; Synchronize (@FSynchronize); end; wherein FSynchronize a record type is: PSynchronizeRecord = ^ TSynchronizeRecord; TSynchronizeRecord = record FThread: TObject; FMethod: TThreadMethod; FSynchronizeException: TObject; end; for the main thread between the thread and the exchange of data comprises an incoming thread class objects, synchronization method, and An exception occurred. The overload version is called in Synchronize, and this overload version is quite special, it is a "class method". The so-called method is a special class member method, and its call does not need to create class instance, but is called by class name as constructor. The reason why it will be used to implement it because it can be called when the thread object is not created. However, it is actually another overload version (also class method) and another class method StaticSynchronize.

Synchronize Here is the code: class procedure TThread.Synchronize (ASyncRec: PSynchronizeRecord); var SyncProc: TSyncProc; begin if GetCurrentThreadID = MainThreadID then ASyncRec.FMethod else begin SyncProc.Signal: = CreateEvent (nil, True, False, nil); try EnterCriticalSection (ThreadLock); try if SyncList = nil then SyncList: = TList.Create; SyncProc.SyncRec: = ASyncRec; SyncList.Add (@SyncProc); SignalSyncEvent; if Assigned (WakeMainThread) then WakeMainThread (SyncProc.SyncRec.FThread) ; LeaveCriticalSection (ThreadLock); try WaitForSingleObject (SyncProc.Signal, INFINITE); finally EnterCriticalSection (ThreadLock); end; finally LeaveCriticalSection (ThreadLock); end; finally CloseHandle (SyncProc.Signal); end; if Assigned (ASyncRec.FSynchronizeException) then Raise asyncRec.fsynchronizeException; end; end; this code is slightly more, but it is not too complicated. The first is to determine if the current thread is a primary thread. If so, simply perform the synchronization method and return. If it is not a main thread, it is ready to start the synchronization process. Recording thread switched data (parameters) and an EVENT HANDLE via a local variable, the record structure is as follows: TsyncProc = Record SyncRec: psynchronizecord; Signal: Thandle; End; then create an Event, then enter the critical area (through global variable threadlock, Because there is only one thread to enter the SYNCHRONIZE state, you can use global variables), and then save this record data into the SyncList (if this list does not exist, you create it). It can be seen that this critical area of ​​Threadlock is to protect access to synclist, which will be seen again when introducing Checksynchronize later. Next, it is called SignalsyncEvent, and its code has been introduced when the constructor of TTHREAD is described earlier, and its function is to simply make SYNCEVENT operations. The use of this SYNCEVENT will be described in detail when WaitFor will be described later. The next step is the most important part: call the WakemainThread event to synchronize.

WakemainThread is a global event for TnotifyEvent type. The reason why the SYNCHRONIZE method is used here is because the Synchronize method is inherently through the message, and the process that needs to be synchronized is executed in the main thread. If there is no message loop, it is unusable. Therefore, use this event to process. In response to this event, the Application object, the following two methods are used to set and empty the response of WakemainThread events (from the Forms unit): Procedure Tapplication.hookynchronizewakeup; begin classes.wakemainthread: = WakemaInThread; end;

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

New Post(0)