Original address: blog.chinaunix.net/uid-2309448…

It is important for beginners to understand mutex, critical sections, semaphores, event flag groups, and message mailboxes:

In order to achieve cooperation and conflict-free operation among tasks, some constraints must be established among related tasks. There are two kinds of restriction relations: direct restriction relation and indirect restriction relation.
Direct constraint relationships result from cooperation between tasks. For example, there are two tasks: Task 1 and task 2, which need to work together by accessing the same data buffer. Task 1 writes data to the buffer, and Task 2 reads data from the buffer. Obviously, task 1 has not written to the buffer (the buffer is empty), and task 2 should wait because it cannot get valid data from the buffer. Task 2 should be told to read the data only after task 1 has written to the buffer. Conversely, when the buffer has not been read by Task 2 (when the buffer is full), task 1 cannot write new data to the buffer and should wait. Task 1 should be told to write data only after task 2 has read from the buffer. It is clear that if the two tasks do not work so harmoniously, there will be serious consequences.
Indirect constraints arise from the sharing of resources. For example, task 1 and task 2 share a printer. If the printer has been assigned to task 1, task 2 should wait because it cannot obtain the right to use the printer. Only after task 1 releases the printer, can the system wake task 2 up and give him access to the printer. If these two tasks do not do so, it will also cause great confusion.
By, in multitasking work in the process of cooperation, the operating system should solve two problems: one is between each task should have a relationship of mutual exclusion, namely for a Shared resource, if a task is to use, can only wait for other tasks, wait until after the task to release the resources, waiting for one of the tasks to use it; Second, related tasks should be executed in sequence. A task can only be carried out after its partner sends a notice or establishes a certain condition, otherwise it can only be carried out after.
This constraining cooperation between tasks is called inter-task synchronization (synchronization, not simultaneous!). .

1 Mutex

1.1 define

A mutex is a common resource that can only be owned by one thread at a given time (the property of ownership), and the thread that owns it can claim the mutex repeatedly.

1.2 purpose

Suitable for controlling thread access to specific resources (e.g., critical sections and shared resources)

A Critical Section is a piece of code that cannot be interrupted by other threads during execution. To protect it, mutex can be used. Examples are as follows:

Status = tx_mutex_get (& my_mutex TX_WAIT_FOREVER);// Apply for a mutex
if(status! =TX_SUCCESS)break;// Check whether the application is successful
tx_thread_slepp(5);// Sleep 5 clock beats
status=tx_mutex_put(&my_mutex);// Release the mutex
Copy the code

The above code is the critical section.

(2) Provide mutually exclusive access to shared resources: If a thread wants exclusive access to a resource, it must provide a corresponding mutex for each resource. The mutex must be acquired before accessing the resource and then released so that other threads can use it to access the resource.

1.3 Problems needing attention

Using mutex can introduce deadlocks and priority inversions.

  1. For example, process A wants to call resource M, but resource M is occupied by process B, and process B wants to call resource N, but resource N is occupied by process A. Both processes send resource requests to the CPU, but do not release their occupied resources. Therefore, both processes are in the suspended state.

How do you avoid deadlock problems? One way to do this is to force each process to have only one mutex at a time. If you must have more than one mutex, you can force threads to apply for the mutex in the same order. For example, if the thread gets mutex 1 first, then mutex 2.

If a thread enters a deadlock state, how can it recover from it? There are two methods:

  • The timeout attribute is used when tx_mutex_get() is called to request a mutex.

  • Use tx_thread_wait_abort() to abort the suspended state of a thread trapped in a deadlock.

  1. Priority inversion problems, such as: A lower priority thread 3 already possess the mutex, at a certain moment at a time when a higher-priority thread 1 to apply for the mutex, thread 1 at this time can only wait and then if there is a priority in the thread 2 is ready, then thread 2 will interrupt threads 3, which will appear thread 2 before the thread 1 and execution, this kind of phenomenon is the priority inversion problems.

    How to avoid priority inversion? You can solve this problem by having the mutex requested by thread 3 use the priority inheritance option. And priority inheritance is: When this happens, a lower priority thread 3 already possess the mutex, at a certain moment at a time when a higher-priority thread 1 to apply for the mutex, the thread will have to wait for 1), the lower priority thread 3, will inherit a higher-priority thread priority threads until after 3 release mutex to restore its priority, so you can avoid the problem of priority inversion.

1.4 One feature of mutex is the ownership property.

2. Critical Section

2.1 define

Access to common resources or a piece of code through the serialization of multiple threads, fast, suitable for controlling data access.

2.2 purpose

A simple way to synchronize threads in the same process to ensure that only one thread can access data at a time, allowing only one thread to access a shared resource at any time. If multiple threads attempt to access a critical section at the same time, all threads attempting to access the critical section will be suspended while one process is in progress and will wait until the thread already in the critical section leaves.

2.3 The difference between mutex and critical section synchronization threads

Although the synchronization speed of the critical section is very fast, it can only be used to synchronize threads within the process, not threads in multiple processes. The mutex is more complex than the critical section. Because using mutex makes it possible to safely share resources not only between different threads of the same application, but also between threads of different applications.

3 semaphore

3.1 define

Each semaphore is a common resource and its value is a 32-bit count. The data structure of a semaphore is a value and a pointer to the next process waiting for the semaphore.

The semaphore value is related to the usage of the corresponding resource. When its value is greater than 0, it indicates the number of available resources. When its value is less than 0, its absolute value represents the number of processes waiting to use the resource. Note that the semaphore value can only be changed by PV operations.

// Implement P, V operation algorithm description:P:while s>0:
        s=s- 1V operation: s= S +1
Copy the code

P means to apply for a resource. If the conditions are met (i.e. the resources that can be allocated to the right), the resource is allocated to the requesting process and the number of resources s is reduced by 1. V indicates that the occupied resources should be released after the completion of the use of resources, and the number of resources s is increased by 1.

3.2 The use of semaphores

  1. Mutex access is similar to mutex access, except that the semaphore does not support ownership, which is at the heart of the mutex. When a semaphore provides mutually exclusive access, the value of the semaphore represents how many threads it allows to access the associated resource simultaneously. In most cases, the semaphore that provides the mutex has an initial value of 1, which means that only one thread can access the resource at any one time, and a semaphore whose value can only be 1 or 0 is generally called a binary semaphore.

When using binary semaphore_get(), the user must prevent a thread that already controls the semaphore from calling tx_semaphore_get() again to request an instance of the semaphore. If this service is invoked, the call fails and the thread is permanently suspended, making the resource unavailable forever.

  1. Event notification: For example, event notification can be provided in producer-consumer mode applications. In this program, the consumer attempts to acquire a semaphore before consuming a resource (such as data in a queue), and the producer increases the value of the semaphore once it has produced the resource. This can be interpreted as the producer giving the instance to the semaphore, and the consumer taking the instance away from the semaphore. Such semaphores typically start at 0 and do not increase in value until the producer produces resources.

The example above illustrates that producers and consumers communicate through semaphores, producers generate event notifications by putting instances into the semaphore (which is the effect of event notifications by changing the value of the semaphore), and consumers wait for event notifications by applying for semaphore instances.

  1. Thread synchronization: In addition to using critical sections and mutex to achieve synchronization between threads, you can also use semaphores, whose signals allow multiple threads to use a shared resource at the same time, indicating the maximum number of threads that can access the shared resource at the same time. There is a counter inside the semaphore. When a thread accesses a shared resource, the counter is automatically decremented. When it reaches zero, no other thread is allowed to access the shared resource until one thread releases the shared resource, thus completing the protection of the shared resource.

Multiple threads competition n (n > 0) resources, such as the four resources, then initialize the quantity to 4, when there is a thread each request semaphore, if the signal is 0, said no resources available, the request of threads wait for it to other contributors to release the semaphore, if the signal quantity greater than zero, the thread of the request is allowed, semaphore is decreasing, The thread can then access the resource by convention, and when the thread has finished using the resource, it should release the semaphore for other threads to use if needed.

3.3 The difference between mutex and semaphore

If mutex is critical to your program, use mutex. If mutex is not the main factor in the program, semaphores are used because they are slightly faster than mutex and use fewer system resources.

3.4 Differences between semaphore event notification and thread synchronization

Semaphore event notification is used between two threads (such as producer-consumer problems), while semaphore thread synchronization is used between multiple threads (such as a phone booth problem where multiple people can make calls).

4 Event flag group

4.1 an overview of the

Event flags can be set and reset by any thread, or viewed by any program, and a thread can be suspended while waiting for a set of event flags to be set. Each event flag is represented by a bit, and every 32 event flags are arranged in a group.

Setting or resetting event flags is done by logically “and” or “or” of the current event flag group and the new flag group; To get the event flag, do a similar logic. Once a flag is set in the event flag group, the system checks the pending queue of the corresponding group, and if it satisfies the pending thread, the thread is restored.

4.2 Application Scenarios

Since an event flag group contains 32 1-bit flags, it can be combined in many ways, so it is more suitable for synchronization of many threads.

5 Message Queue

5.1 an overview of the

First it is a queue, so it is a data structure, but it is also a “message queue”, so it is a data structure that can pass multiple messages (messages are data, which are actually Pointers to messages).

5.2 the use of

If between threads and thread to pass a lot of information (data), so in order to adapt to the needs of different data, it is best to establish multiple data in the memory buffer, to transmit data in the buffer, which respectively in the message queue to store the message buffer address, so that it can realize data communication between threads. Ideal for situations where a lot of data is passed between threads

5.3 The difference between message queue interthread communication and semaphore thread synchronization

Interthread communication in message queues is used to pass data, whereas thread synchronization in semaphores focuses on the indicative role of semaphores.

6 application

6.1 An application of mutex and binary semaphore:

A phone booth that allows only one user at a time should have such a sign on the door to indicate the use of the booth in order to prevent conflicts between users. For example, use a two-color sign, red for “nobody” and green for “nobody.” In this way, when people see that the color on the sign is green (the thread knows the semaphore value is 1 by querying), they can go in and make a call (indicating that a process can occupy the resource (phone booth)). If it’s red, you have to wait. If more people start to show up, wait in line. The name on the phone booth is a semaphore or mutex.

6.2 An example of semaphore synchronization thread:

A phone booth can allow multiple people (threads) to make calls, and the counter on the phone booth door automatically decreases by one for each person who enters and increases by one for each person who leaves. The initial value on the counter is the maximum number of phone calls that the booth can hold, so as long as you see the counter value greater than 0, you can go in to make a call; Otherwise, you have to wait. Where this counter is the semaphore used to synchronize threads.