Multi-thread and synchronization in Linux and multi-thread synchronization in Linux

Source: Internet
Author: User

Multi-thread and synchronization in Linux and multi-thread synchronization in Linux

Author: Vamei Source: http://www.cnblogs.com/vamei welcome reprint, please also keep this statement. Thank you!

 

A typical UNIX system supports multiple threads in a process ). As mentioned in the Linux Process basics, Linux organizes operations in process units, and Linux threads are also based on processes. Although the implementation method is different from other UNIX systems, the logic and usage of Linux multithreading is no different from that of real multithreading.

 

Multithreading

Let's first look at what multithreading is. In Linux, from the program to the process, we see a program in the memory. Only one control exists during the entire running process of this program. When a function is called, the function obtains control, becomes an active function, and then runs the commands in the function. At the same time, other functions are in the off-site status and do not run. As shown in:

Linux from program to process

 

We can see that each square block is connected by an arrow. Each function is connected to an online system. A computer executes the operations defined in each function just like a pipeline. Such a program is called a single-threaded program.


Multithreading allows multiple controls in a process so that multiple functions are activated at the same time, so that the operations of multiple functions can run at the same time. Even a single-CPU computer can continuously switch between commands of different threads, resulting in the simultaneous running of multiple threads. As shown in, it is a multi-threaded process:

Main () to func3 () to main () to form a thread. In addition, func1 () and func2 () constitute two other threads. The operating system generally has some system calls that allow you to run a function into a new thread.

 

Recall the functions and usage of the stack mentioned in Linux from program to process. Only the Bottom Frame of a stack can be read and written. Correspondingly, only the function corresponding to the frame is activated and in the working state. To implement multithreading, We must bypass the stack restrictions. Therefore, when creating a new thread, we will create a new stack for this thread. Each stack corresponds to a thread. When a stack is executed to all pops up, the corresponding thread completes the task and closes the job. Therefore, multi-threaded processes have multiple stacks in the memory. Multiple stacks are separated by some blank areas for Stack growth. Each thread can call the parameters and variables in the bottom Frame of its stack, and share the Text, heap, and global data areas with other threads in the memory. In the preceding example, three stacks are required in the process space.

(It should be noted that, for multithreading, because multiple stacks exist in the same process space, filling any blank area will cause the stack overflow problem .)

 

Concurrency

Multithreading is equivalent to a concunrrency system. A concurrent system generally executes multiple tasks at the same time. If multiple tasks can share resources, especially when writing a variable at the same time, the synchronization problem needs to be solved. For example, we have a multi-thread train ticket sales system that uses global variable I to store the remaining votes. Multiple Threads continuously sell tickets (I = I-1) until the remaining number of votes is 0. Therefore, you must perform the following operations:

/*mu is a global mutex*/

while (1) {    /*infinite loop*/ if (i != 0) i = i -1 else { printf("no more tickets"); exit(); }}

If only one thread executes the above Program (equivalent to a window ticket), there is no problem. But if multiple threads execute the above Program (equivalent to selling tickets in multiple windows), we will have a problem. We can see that the root cause is that all threads simultaneously can read and write data to I.

Here, the if structure will give two instructions to the CPU, one is to determine whether there is any remaining ticket (I! = 0). One is to sell tickets (I = I-1 ). A thread first checks whether there is a ticket (for example, I is 1 at this time), but there is a time window between the two commands, other threads may execute the ticket selling operation (I = I-1) within this time window, resulting in the condition that the thread will not sell tickets. However, this thread has already executed the judgment command, so it is impossible to know That I has changed, so it continues to execute the ticket selling command, so that it can sell non-existent tickets (I becomes a negative number ). For a real ticket sales system, this will become a serious mistake (too many tickets are sold and the train is full ).

In the case of concurrency, the sequence of command execution is determined by the kernel. Within the same thread, commands are executed sequentially, but it is hard to say which one of the commands between different threads will be executed first. If the running result depends on the execution sequence of different threads, it will lead to race condition. Under such circumstances, the computer's results are hard to predict. We should try to avoid the formation of competitive conditions. The most common way to solve the competition condition is to create an atomic operation (atomic operation) that cannot be separated by the two commands, while other tasks cannot be inserted into the atomic operation.

 

Multi-thread synchronization

For multi-threaded programs, synchronization allows only one thread to access a certain resource within a certain period of time. During this time, other threads are not allowed to access the resource. We can use mutex, condition variable, and reader-writer lock to synchronize resources.

 

1) mutex lock

A mutex is a special variable that has two states: lock and unlock. Mutex lock is generally set as a global variable. The mutex lock opened can be obtained by a thread. Once obtained, the mutex lock is locked and only the thread has the right to open it. Other threads that want to obtain the mutex lock will wait until the mutex lock is opened again. We can think of mutex lock as a restroom that can only accommodate one person. When a person enters the restroom, he can lock the restroom from it. Other people can only wait for the person to come out outside the mutex lock. The persons waiting outside are not in the queue. If the restroom is empty, they can rush in first.

The above problem is easily solved by using mutex lock. The program of each thread can be changed:

/*mu is a global mutex*/

while (1) { /*infinite loop*/ mutex_lock(mu);       /*aquire mutex and lock it, if cannot, wait until mutex is unblocked*/ if (i != 0) i = i - 1; else { printf("no more tickets"); exit(); } mutex_unlock(mu);     /*release mutex, make it unblocked*/}

The first thread that executes mutex_lock () will first obtain mu. Other threads that want to obtain mu must wait until the first thread executes mutex_unlock () to release mu to obtain mu and continue executing the thread. Therefore, when a thread operates between mutex_lock () and mutex_unlock (), it is not affected by other threads and thus constitutes an atomic operation.

Note that if a thread still uses the original program (I .e., directly modifying I without trying to obtain mu), the mutex lock cannot prevent the program from modifying I, the mutex lock loses the meaning of resource protection. Therefore, the programmer must write a complete program to implement the mutex lock function. The same applies to other mechanisms.

 

2) condition Variables

Conditional variables are another common variable. It is often stored as a global variable and cooperates with the mutex lock.

 

Suppose there are 100 workers who are responsible for decorating a room. When 10 rooms are finished, the boss will notify the corresponding 10 workers to drink beer together.

How can we implement it? The boss asked the workers to check the number of rooms that had been decorated after the room was decorated. However, multi-threaded environments pose a risk of competition. That is to say, other workers may finish the work between the house decoration and inspection. The solution is as follows:

/*mu: global mutex, cond: global codition variable, num: global int*/
mutex_lock(mu)num = num + 1; /*worker build the room*/if (num <= 10) { /*worker is within the first 10 to finish*/ cond_wait(mu, cond);     /*wait*/ printf("drink beer");}else if (num = 11) { /*workder is the 11th to finish*/ cond_broadcast(mu, cond);        /*inform the other 9 to wake up*/}mutex_unlock(mu);

Conditional variables are used above. In addition to the mutex lock, the condition variable also needs to work with another global variable (num here, that is, the number of renovated rooms ). This global variable is used to form various conditions.

 

The specific ideas are as follows. We asked the workers to check the number of rooms (num <10) that have been renovated after the room was renovated (num = num + 1 ). Since mu is locked, no other workers will renovate the room during this period (changing the num value ). If the worker is the top 10 completed workers, call the cond_wait () function.
Cond_wait () does two things. One is to release mu so that other workers can build houses. The other is waiting until the notification of cond. In this way, the qualified thread starts to wait.

When a notification is sent (the tenth room has been built), condwait () locks mu again. Run prinft ("drink beer") (beer!) to resume the thread !). From here until mutex_unlock (), it constitutes another mutex lock structure.

So how do the first 10 threads that call cond_wait () Receive notifications? We noticed that elif if, that is, the person who built the 11th room, is responsible for calling cond_broadcast (). This function will send a notification to all threads that call cond_wait (), so that those threads can resume operation.

 

Conditional variables are especially suitable for multiple threads waiting for a condition. If condition variables are not used, each thread must constantly try to obtain the mutex lock and check whether the conditions occur. This greatly wastes system resources.

 

3) read/write lock

Read/write locks are very similar to mutex locks. R and RW locks are available in three states: shared-read, exclusive-write lock, and unlock ). The last two States are identical to the previous mutex lock.

An unlock RW lock can be used by a thread to obtain the R lock or W lock.

If the R lock is obtained by a thread, RW lock can continue to be obtained by other threads without waiting for the thread to release the R lock. However, if another thread wants to obtain the W lock, it must wait until all threads holding the shared read lock release their R locks.

If a lock is obtained by a thread, other threads, whether they want to obtain the R lock or W lock, must wait for the thread to release the W lock.

In this way, multiple threads can read shared resources at the same time. Risky write operations are protected by mutex locks.

 

We need a synchronous concurrency system, which makes programming difficult for programmers. However, multi-threaded systems can solve many IO bottlenecks. For example, we listen on the network port. If we only have one thread, we must listen, receive, process, reply, and listen again. If we use a multi-threaded system, multiple threads can be monitored. When processing a thread, we can continue listening to other threads, which greatly improves the system utilization. As the data grows and the number of read/write operations on servers increases, this is of great significance. Multithreading can also make more effective use of multi-CPU environments.

(Just like cooking, constantly switch to different dishes .)

 

The program in this article adopts the pseudo-C method. Different Languages have different function names (such as mutex_lock ). Here we focus on logical concepts rather than specific implementations and language specifications.

 

Summary

Multiple threads, multiple stacks

Race condition

Mutex, condition variable, RW lock

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.