Linux kernel critical section, mutex, semaphore

1. Critical section: Access public resources or a piece of code through serialization of multi-threads. It is fast and suitable for controlling data access. 2. Mutex: Designed to coordinate individual access to a shared resource. 3. Semaphore: Designed to control a resource with a limited number of users.

Critical Section

A simple way to ensure that only one thread can access data at a time. Only one thread is allowed to access shared resources at any time. If multiple threads try to access the critical section at the same time, then after one thread enters, all other threads trying to access the critical section will be suspended until the thread entering the critical section leaves. After the critical section is released, other threads can continue to preempt it, thereby achieving the purpose of atomically operating shared resources.

The critical section contains two operating primitives:

EnterCriticalSection() enters the critical section LeaveCriticalSection() leaves the critical section

After the EnterCriticalSection() statement is executed, the code will enter the critical section. No matter what happens, you must ensure that the matching LeaveCriticalSection() can be executed. Otherwise the shared resources protected by the critical section will never be released. Although critical section synchronization is very fast, it can only be used to synchronize threads within this process, and cannot be used to synchronize threads in multiple processes.

MFC provides many fully functional classes. I used MFC to implement the critical section. MFC provides a CCriticalSection class for critical sections. It is very simple to use this class for thread synchronization. Just use the CCriticalSection class member functions Lock() and UnLock() in the thread function to demarcate the protected code fragment. The resources used by the code after Lock() are automatically regarded as resources in the critical section and are protected. Only after UnLock can other threads access these resources.

//CriticalSection

CCriticalSection global_CriticalSection;

  // Share resource

char global_Array[256];

//Initialize shared resources

void InitializeArray()

{

? for(int i = 0;i<256;i + + )

{

? global_Array[i]=I;

}

}

//Write thread

UINT Global_ThreadWrite(LPVOID pParam)

{

CEdit *ptr=(CEdit *)pParam;

ptr->SetWindowText("");

//Enter the critical section

global_CriticalSection.Lock();

? for(int i = 0;i<256;i + + )

{

? global_Array[i]=W;

ptr->SetWindowText(global_Array);

Sleep(10);

}

//Leave the critical section

? global_CriticalSection.Unlock();

Return 0;

}

//Delete thread

UINT Global_ThreadDelete(LPVOID pParam)

{

CEdit *ptr=(CEdit *)pParam;

ptr->SetWindowText("");

//Enter the critical section

? global_CriticalSection.Lock();

? for(int i = 0;i<256;i + + )

{

global_Array[i]=D;

ptr->SetWindowText(global_Array);

Sleep(10);

}

//Leave the critical section

? global_CriticalSection.Unlock();

Return 0;

}

//Create a thread and start the thread

void CCriticalSectionsDlg::OnBnClickedButtonLock()

{

? //Start the first Thread

CWinThread *ptrWrite = AfxBeginThread(Global_ThreadWrite,

&m_Write,

THREAD_PRIORITY_NORMAL,

0,

CREATE_SUSPENDED);

ptrWrite->ResumeThread();

? //Start the second Thread

CWinThread *ptrDelete = AfxBeginThread(Global_ThreadDelete,

&m_Delete,

THREAD_PRIORITY_NORMAL,

0,

CREATE_SUSPENDED);

ptrDelete->ResumeThread();

}

In the test program, the two buttons Lock and UnLock are implemented respectively, the execution state of shared resources with critical section protection, and the execution state of shared resources without critical section protection.

Mutex

A mutex is very similar to a critical section. Only the thread that owns the mutex object has the permission to access the resource. Since there is only one mutex object, it is determined that this shared resource will not be accessed by multiple threads at the same time under any circumstances. . The thread currently occupying the resource should hand over the mutex it owns after the task is processed, so that other threads can access the resource after acquiring it. Mutexes are more complex than critical sections. Because the use of mutual exclusion can not only achieve safe sharing of resources among different threads of the same application, but also achieve safe sharing of resources between threads of different applications.

Mutex contains several operation primitives:

CreateMutex() creates a mutex

OpenMutex() opens a mutex

ReleaseMutex() releases the mutex

WaitForMultipleObjects() waits for mutex objects

Similarly, MFC provides a CMutex class for mutexes. It is very simple to use the CMutex class to implement mutex operations, but special attention should be paid to the call to the CMutex constructor.

CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL)

Do not fill in unused parameters randomly. Filling in random parameters will cause some unexpected operation results.

//Create a mutex

CMutex global_Mutex(0,0,0);

  // Share resource

char global_Array[256];

void InitializeArray()

{

? for(int i = 0;i<256;i + + )

{

? global_Array[i]=I;

}

}

UINT Global_ThreadWrite(LPVOID pParam)

{

CEdit *ptr=(CEdit *)pParam;

ptr->SetWindowText("");

? global_Mutex.Lock();

? for(int i = 0;i<256;i + + )

{

? global_Array[i]=W;

ptr->SetWindowText(global_Array);

Sleep(10);

}

? global_Mutex.Unlock();

Return 0;

}

UINT Global_ThreadDelete(LPVOID pParam)

{

CEdit *ptr=(CEdit *)pParam;

ptr->SetWindowText("");

? global_Mutex.Lock();

? for(int i = 0;i<256;i + + )

{

? global_Array[i]=D;

ptr->SetWindowText(global_Array);

Sleep(10);

}

? global_Mutex.Unlock();

Return 0;

}

Also in the test program, the two buttons Lock and UnLock are implemented respectively, in the execution state with a mutex protecting shared resources, and in the execution state without a mutex protecting shared resources.

Semaphores

The way the semaphore object synchronizes threads is different from the previous methods. The signal allows multiple threads to use shared resources at the same time, which is the same as the PV operation in the operating system. It indicates the maximum number of threads that can access shared resources at the same time. It allows multiple threads to access the same resource at the same time, but needs to limit the maximum number of threads that can access this resource at the same time. When creating a semaphore with CreateSemaphore(), you must also indicate the maximum allowed resource count and the currently available resource count. Generally, the current available resource count is set to the maximum resource count. Each time an additional thread accesses the shared resource, the current available resource count will be 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 decreases to 0, it means that the number of threads currently occupying resources has reached the maximum allowed number, and other threads cannot be allowed to enter. At this time, the semaphore signal will not be sent. After the thread has finished processing the shared resources, it should increase the current available resource count by 1 through the ReleaseSemaphore() function while leaving. The current available resource count can never be greater than the maximum resource count at any time.

The concepts of PV operation and semaphore were proposed by Dutch scientist E.W. Dijkstra. The semaphore S is an integer. When S is greater than or equal to zero, it represents the number of resource entities available for concurrent processes. However, when S is less than zero, it represents the number of processes that are waiting to use shared resources.

P operation application resources:

(1) S minus 1;

(2) If S is still greater than or equal to zero after decrementing 1, the process continues to execute;

(3) If S is less than zero after minus 1, the process will be blocked and enter the queue corresponding to the signal, and then transfer to process scheduling.

V operation releases resources:

(1) S plus 1;

(2) If the addition result is greater than zero, the process continues to execute;

(3) If the addition result is less than or equal to zero, wake up a waiting process from the waiting queue of the signal, and then return to the original process to continue execution or transfer to process scheduling.

Semaphore contains several operation primitives:

CreateSemaphore() creates a semaphore

OpenSemaphore() opens a semaphore

ReleaseSemaphore() releases the semaphore

WaitForSingleObject() waits for the semaphore

//Semaphore handle

HANDLE global_Semephore;

  // Share resource

char global_Array[256];

void InitializeArray()

{

? for(int i = 0;i<256;i + + )

{

? global_Array[i]=I;

}

}

//Thread 1

UINT Global_ThreadOne(LPVOID pParam)

{

CEdit *ptr=(CEdit *)pParam;

ptr->SetWindowText("");

//Waiting for the shared resource request to be passed, equal to the P operation

?WaitForSingleObject(global_Semephore, INFINITE);

? for(int i = 0;i<256;i + + )

{

global_Array[i]=O;

ptr->SetWindowText(global_Array);

Sleep(10);

}

//Release shared resources equal to V operation

ReleaseSemaphore(global_Semaphore, 1, NULL);

Return 0;

}

UINT Global_ThreadTwo(LPVOID pParam)

{

CEdit *ptr=(CEdit *)pParam;

ptr->SetWindowText("");

WaitForSingleObject(global_Semephore, INFINITE);

? for(int i = 0;i<256;i + + )

{

? global_Array[i]=T;

ptr->SetWindowText(global_Array);

Sleep(10);

}

ReleaseSemaphore(global_Semaphore, 1, NULL);

Return 0;

}

UINT Global_ThreadThree(LPVOID pParam)

{

CEdit *ptr=(CEdit *)pParam;

ptr->SetWindowText("");

WaitForSingleObject(global_Semephore, INFINITE);

? for(int i = 0;i<256;i + + )

{

global_Array[i]=H;

ptr->SetWindowText(global_Array);

Sleep(10);

}

ReleaseSemaphore(global_Semaphore, 1, NULL);

Return 0;

}

void CSemaphoreDlg::OnBnClickedButtonOne()

{

//Set the semaphore 1 resource 1 which can only be accessed by one thread at the same time

global_Semephore= CreateSemaphore(NULL, 1, 1, NULL);

? this->StartThread();

// TODO: Add your control notification handler code here

}

void CSemaphoreDlg::OnBnClickedButtonTwo()

{

//Set the semaphore 2 resources 2, which can only be accessed by two threads at the same time

? global_Semephore= CreateSemaphore(NULL, 2, 2, NULL);

? this->StartThread();

// TODO: Add your control notification handler code here

}

void CSemaphoreDlg::OnBnClickedButtonThree()

{

//Set semaphore 3 resources 3, which can only be accessed by three threads at the same time

global_Semephore= CreateSemaphore(NULL, 3, 3, NULL);

? this->StartThread();

// TODO: Add your control notification handler code here

}

The usage characteristics of semaphores make it more suitable for synchronizing threads in Socket programs. For example, the HTTP server on the network needs to limit the number of users who access the same page at the same time. In this case, a thread can be set up for each user’s page request to the server, and the page is a shared resource to be protected. By using signals The amount of synchronization of threads can ensure that no matter how many users access a certain page at any time, only threads that are not greater than the set maximum number of users can access, while other access attempts are suspended. Only when there are It is possible to enter only after the user exits from accessing this page.

Summary:

1. The role of a mutex is very similar to that of a critical section, but the mutex can be named, which means it can be used across processes. Therefore, creating a mutex requires more resources, so if it is only used within the process, using the critical section will bring speed advantages and reduce resource usage. Because the mutex is a cross-process mutex, once it is created, it can be opened by name.

2. Mutex (Mutex), semaphore (Semaphore), and event (Event) can all be used across processes to perform synchronized data operations. Other objects have nothing to do with data synchronization operations, but for processes and threads, if processes and threads In the running state, there is no signal state, and after exiting, there is a signal state. So you can use WaitForSingleObject to wait for processes and threads to exit.

3. A mutex can be used to specify that resources are used in an exclusive manner, but if there is one of the following situations that cannot be handled by a mutex, for example, a user has purchased a three-concurrent access license
The database system can determine how many threads/processes can perform database operations at the same time based on the number of access licenses purchased by the user. At this time, there is no way to complete this requirement if a mutex is used. The semaphore object can be said to be a resource counter.

Original author: Rhubarb Rhubarb Rhubarb

Original address: https://cloud.tencent.com/developer/article/1339623 (The copyright belongs to the author of the original article, please contact us to delete any infringement messages)