The method for synchronizing semaphores (semaphore) kernel objects to threads is different from the previous methods. It allows multiple threads to access the same resource at the same time, however, you must limit the maximum number of threads that can access this resource at the same time. When using createsemaphore () to create a semaphore, you must specify the maximum allowed resource count and the current available resource count. Generally, the current available resource count is set to the maximum Resource Count. Each time a thread is added to access a shared resource, the current available resource count is reduced by 1, as long as the current available resource count is greater than 0, a semaphore signal can be sent. However, when the current available count is reduced to 0, it indicates that the number of threads currently occupying resources has reached the maximum allowed number, and other threads cannot enter, at this time, the semaphore signal cannot be sent. After processing shared resources, the thread should use the releasesemaphore () function to increase the number of currently available resources by 1 while leaving. The current available resource count cannot exceed the maximum resource count at any time.
Semaphores control thread access resources by counting. In fact, semaphores are also called Dijkstra counters.
Functions such as createsemaphore (), opensemaphore (), releasesemaphore (), waitforsingleobject (), and waitformultipleobjects () are used for thread synchronization using semaphores kernel objects. Createsemaphore () is used to create a semaphore kernel object. Its function prototype is:
Handle createsemaphore (
Lpsecurity_attributes l1_maphoreattributes, // Security Attribute pointer
Long linitialcount, // initial count
Long lmaximumcount, // maximum count
Lptstr lpname // Object Name Pointer
);
The lmaximumcount parameter is a signed 32-bit value that defines the maximum allowed Resource Count. The maximum value cannot exceed 4294967295. The lpname parameter can define a name for the created semaphore. because it creates a kernel object, this semaphore can be obtained through this name in other processes. The opensemaphore () function can be used to open the semaphore created in other processes based on the semaphore name. The function prototype is as follows:
Handle opensemaphore (
DWORD dwdesiredaccess, // access flag
Bool binherithandle, // inheritance flag
Lptstr lpname // semaphore name
);
When the thread leaves the processing of shared resources, it must use releasesemaphore () to increase the current available resource count. Otherwise, the actual number of threads currently processing shared resources does not reach the value to be limited, but other threads still cannot enter because the current number of available resources is 0. The function prototype of releasesemaphore () is:
Bool releasesemaphore (
Handle hsemaphore, // semaphore handle
Long lreleasecount, // increase the count
Lplong lppreviouscount // previous count
);
This function adds the value in lreleasecount to the current resource count of the semaphore. Generally, it sets lreleasecount to 1. You can also set other values if needed. Waitforsingleobject () and waitformultipleobjects () are mainly used at the entrance of the thread function trying to enter the shared resource. They are mainly used to determine whether the current available resource count of the semaphore allows the thread to enter. Only when the current available resource count is greater than 0 will the monitored semaphore kernel object be notified.
The usage of semaphores makes it more suitable for synchronization of threads in socket (socket) programs. For example, if the HTTP server on the network needs to limit the number of users who access the same page at the same time, you can set a thread for none of the users to request the page on the server, the page is the shared resource to be protected. By using semaphores to synchronize threads, users can access a page no matter how many users at any time, only threads with the maximum number of users can access this page, while other access attempts are suspended. This page can only be accessed after a user exits. The sample code below demonstrates a similar process:
// Semaphore object handle
Handle hsemaphore;
Uint threadproc15 (lpvoid pparam)
{
// Try to enter the semaphore threshold
Waitforsingleobject (hsemaphore, infinite );
// Thread Task Processing
Afxmessagebox ("the thread is being executed! ");
// Release semaphore count
Releasesemaphore (hsemaphore, 1, null );
Return 0;
}
Uint threadproc16 (lpvoid pparam)
{
// Try to enter the semaphore threshold
Waitforsingleobject (hsemaphore, infinite );
// Thread Task Processing
Afxmessagebox ("thread 2 is being executed! ");
// Release semaphore count
Releasesemaphore (hsemaphore, 1, null );
Return 0;
}
Uint threadproc17 (lpvoid pparam)
{
// Try to enter the semaphore threshold
Waitforsingleobject (hsemaphore, infinite );
// Thread Task Processing
Afxmessagebox ("thread 3 is being executed! ");
// Release semaphore count
Releasesemaphore (hsemaphore, 1, null );
Return 0;
}
......
Void csample08view: onsemaphore ()
{
// Create a semaphore object
Hsemaphore = createsemaphore (null, 2, 2, null );
// Enable the thread
Afxbeginthread (threadproc15, null );
Afxbeginthread (threadproc16, null );
Afxbeginthread (threadproc17, null );
}
The above code first creates a semaphore object hsemaphore whose initial count and maximum Resource Count are both 2 before enabling the thread. That is, at the same time, only two threads are allowed to enter the shared resources protected by hsemaphore. The three threads enabled subsequently attempt to access the shared resource. When the first two threads attempt to access the shared resource, the current available resource count of hsemaphore is 2 and 1, respectively, at this time, hsemaphore can be notified, that is, waitforsingleobject () at the thread entrance will be returned immediately. After the first two threads enter the protection area, the current resource count of hsemaphore is reduced to 0, hsemaphore will not be notified, and waitforsingleobject () will suspend the thread. It is not allowed to enter until the thread that previously enters the protected area exits. Figure 4 and figure 5 show the running results of the above channels. From the experiment results, we can see that the semaphore always keeps up with two threads at the same time.
In MFC, semaphores are expressed through the csemaphore class. This class only has one constructor. You can construct a semaphore object and initialize the initial resource count, maximum Resource Count, Object Name, and Security Attribute. Its prototype is as follows:
Csemaphore (long linitialcount = 1, long lmaxcount = 1, lpctstr pstrname = NULL, lpsecurity_attributes lpsaattributes = NULL );
After a csemaphore class object is constructed, any thread that accesses protected shared resources must inherit the lock () and unlock () obtained from the parent class csyncobject through csemaphore () to access or release the csemaphore object. Similar to the methods described earlier to maintain Thread Synchronization Through the MFC class, the preceding thread synchronization code can also be rewritten through the csemaphore class, the two thread synchronization methods that use semaphores are completely consistent in implementation principle and in implementation results. The following is the semaphore thread synchronization code modified by MFC:
// MFC semaphore object
Csemaphore g_clssemaphore (2, 2 );
Uint threadproc24 (lpvoid pparam)
{
// Try to enter the semaphore threshold
G_clssemaphore.lock ();
// Thread Task Processing
Afxmessagebox ("the thread is being executed! ");
// Release semaphore count
G_clssemaphore.unlock ();
Return 0;
}
Uint threadproc25 (lpvoid pparam)
{
// Try to enter the semaphore threshold
G_clssemaphore.lock ();
// Thread Task Processing
Afxmessagebox ("thread 2 is being executed! ");
// Release semaphore count
G_clssemaphore.unlock ();
Return 0;
}
Uint threadproc26 (lpvoid pparam)
{
// Try to enter the semaphore threshold
G_clssemaphore.lock ();
// Thread Task Processing
Afxmessagebox ("thread 3 is being executed! ");
// Release semaphore count
G_clssemaphore.unlock ();
Return 0;
}
......
Void csample08view: onsemaphoremfc ()
{
// Enable the thread
Afxbeginthread (threadproc24, null );
Afxbeginthread (threadproc25, null );
Afxbeginthread (threadproc26, null );
}