POSIX multi-thread programming

Source: Internet
Author: User
POSIX multi-thread programming
1. advantages over multi-process
Some form of additional kernel overhead is imposed to reduce performance.

In most cases, IPC is not a "natural" extension of code. Generally, the complexity of the program is greatly increased.

If you want to write portable multi-threaded code, the code can run on Solaris, FreeBSD, Linux, and other platforms. POSIX Threads are of course a choice.

2. Two important issues.
The first question is how the main thread runs after the new thread is created. The main thread continues to execute the next program in sequence (in this example, if (pthread_join (...))").

The second problem is how to deal with the new thread end. The answer is that the new thread stops first and then waits for merging or "connecting" with another thread as part of its cleaning process ".

When thread_function () is completed, pthread_join () returns. At this time, the program has only one main thread. When the program exits, all new threads have been merged using pthread_join. This is how to process each new thread created in the program. If a new thread is not merged, the maximum number of threads in the system is still limited. This means that if the thread is not properly cleared, the call to pthread_create () will eventually fail.

This hierarchy does not exist in POSIX Threads. Although the main thread can create a new thread, the new thread can create another new thread. POSIX thread standards regard them as equivalent layers. So the concept of waiting for the sub-thread to exit is meaningless here. POSIX thread standards do not record any "family" information. The lack of family information has a major implication: If you want to wait for a thread to terminate, you must pass the thread TID to pthread_join (). The thread library cannot determine tid for you.

This is not good news for most developers because it will complicate programs with multiple threads. But don't worry about it. POSIX thread standards provide all the tools required to effectively manage multiple threads. In fact, the fact that there is no parent/child relationship opens up more creative ways to use threads in programs. For example, if a thread is called thread 1 and thread 1 creates a thread called thread 2, thread 1 itself does not need to call pthread_join () to merge thread 2, any other thread in the program can do this. When writing a large number of code using threads, this may allow interesting things. For example, you can create a global "Dead thread list" that contains all stopped threads, and then add a dedicated thread to the list. This cleanup thread calls pthread_join () to merge the just-stopped thread with itself. Now, only one thread is used to skillfully and effectively process all cleanup operations.

 

3. Function Interfaces

3.1 thread_create
Let's take a look at the pthread_create parameter.
The first parameter & mythread is a pointer to mythread.
The second parameter is currently null and can be used to define certain attributes of a thread. Because the default thread attribute is applicable, you only need to set this parameter to null.
The third parameter is the name of the function called when the new thread starts. Note that thread_function () accepts void * as the parameter, and the return value type is also void *. This indicates that void * can be used to transmit any type of data to the new thread, and any type of data can be returned when the new thread completes.
So how to pass an arbitrary parameter to the thread? Very simple. You only need to use the fourth parameter in pthread_create. In this example, because there is no need to pass any data to the insignificant thread_function (), set the fourth parameter to null.

3.2 thread_join

Just as pthread_create () Splits a thread into two, pthread_join () merges the two threads into one.
The first parameter of pthread_join () is TID mythread.
The second parameter is the pointer to the void pointer. If the void pointer is not null, pthread_join places the void * return value of the thread at the specified position. Because we do not need to care about the return value of thread_function (), we set it to null.

3.3 pthread_mutex_lock () and pthread_mutex_unlock ()

Pthread_mutex_lock () and pthread_mutex_unlock () are usually used to protect the data structure. This means that, by locking and unlocking a thread, only one thread can access a Data Structure at a certain time point. It can be inferred that when the thread attempts to lock an unlocked mutex object, the POSIX thread library will agree to lock the object without putting the thread into sleep state.

Pthread_mutex_lock (pthread_mutex_t * mutex)
Pthread_mutex_lock () accepts a pointer to a mutex object as a parameter to lock it. If a mutex object is locked, the caller enters the sleep state. When the function returns, it will wake up the caller (apparently) and the caller will keep the lock. If the function is successfully called, zero is returned. If the function fails, a non-zero error code is returned.

Pthread_mutex_unlock (pthread_mutex_t * mutex)
Pthread_mutex_unlock () works with pthread_mutex_lock () to unlock mutex objects that have been locked by the thread. Always unlock mutex objects that have been locked as soon as possible (to improve performance ). Do not unlock mutex objects that you have not kept locked (otherwise, the call to pthread_mutex_unlock () will fail and bring a non-zero eperm return value ).

Pthread_mutex_trylock (pthread_mutex_t * mutex)
When the thread is doing other things (because the mutex object is currently locked), this call is quite convenient if you want to lock the mutex object. When pthread_mutex_trylock () is called, The system attempts to lock the mutex object. If the mutex object is currently unlocked, you will obtain the lock and the function will return zero. However, if the mutex object is locked, this call will not be blocked. Of course, it will return a non-zero ebusy error value. Then you can continue to do other things and try to lock it later.

 

 

3.4 pthread_mutex_init and pthread_mutex_destroy ()

As shown in, pthread_mutex_init accepts a pointer as a parameter to initialize as a mutex object, which points to an allocated memory area. The second parameter can accept an optional pthread_mutexattr_t pointer. This structure can be used to set various mutex object attributes. However, these attributes are usually not required, so the normal practice is to specify NULL.

Once a mutex object is initialized using pthread_mutex_init (), use pthread_mutex_destroy () to remove it. Pthread_mutex_destroy () accepts a pointer to pthread_mutext_t as a parameter, and releases any resources assigned to it when a mutex object is created. Note that pthread_mutex_destroy () will not release the memory used to store pthread_mutex_t. Releasing your memory is entirely dependent on you. Note that, when pthread_mutex_init () and pthread_mutex_destroy () are successful, zero is returned.

3.5

4. Mutual Exclusion
 
This is how mutex objects work. If thread a tries to lock a mutex object, and thread B has locked the same mutex object, thread a enters the sleep state. Once thread B releases the mutex object (called through pthread_mutex_unlock (), thread a can lock the mutex object (in other words, thread a will return the mutex object from the call of the pthread_mutex_lock () function, at the same time, the mutex object is locked ). Similarly, when thread a is locking the mutex object, if thread C tries to lock the mutex object, thread C will temporarily enter the sleep state. All threads that call pthread_mutex_lock () on the locked mutex object will enter the sleep state. These sleeping threads will "queue" to access this mutex object.

Pthread_mutex_lock () and pthread_mutex_unlock () are usually used to protect the data structure. This means that, by locking and unlocking a thread, only one thread can access a Data Structure at a certain time point. It can be inferred that when the thread attempts to lock an unlocked mutex object, the POSIX thread library will agree to lock the object without putting the thread into sleep state.

 

If too many mutex objects are placed, the Code has no concurrency, and the running process is slower than the single-thread solution. If too few mutex objects are placed, the Code may encounter strange and embarrassing errors. Fortunately, there is an intermediate position. First, the mutex object is used for serializing access to * shared data *. Do not use mutex objects for non-shared data. If the program logic ensures that only one thread can access a specific data structure at any time, do not use mutex objects.

Second, if you want to use shared data, you should use mutex when reading and writing shared data. Use pthread_mutex_lock () and pthread_mutex_unlock () to protect the read/write part, or use them randomly in an unfixed place in the program. Learn to examine the code from the perspective of a thread and ensure that each thread in the program has a consistent and appropriate idea of memory. To get familiar with the usage of mutex objects, it may take several hours to write code at first, but soon it will get used to it and ** you don't have to think about it to use them correctly.

Now let's take a look at the various methods for using mutex objects. Let's start with initialization. In the thread3.c example, we use the static initialization method. This requires the declaration of a pthread_mutex_t variable and the constant pthread_mutex_initializer:

Pthread_mutex_t mymutex = pthread_mutex_initializer;

It's easy. However, you can also dynamically create mutex objects. This dynamic method is used when the Code uses malloc () to allocate a new mutex object. In this case, the static initialization method does not work, and the routine pthread_mutex_init () should be used ():

Int pthread_mutex_init (pthread_mutex_t * mymutex, const pthread_mutexattr_t * ATTR)

 

5 thread insider

5.1

Before explaining how to determine where to use mutex, let's take a deeper look at the internal working mechanism of the thread. See the First example:

Assume that the main thread will create three new threads: thread a, thread B, and thread C. Assume that thread a is created first, thread B is created, and thread C is created.

Pthread_create (& thread_a, null, thread_function, null );
Pthread_create (& thread_ B, null, thread_function, null );
Pthread_create (& thread_c, null, thread_function, null );

After the first pthread_create () call is complete, it can be assumed that thread a is either existing or ended and stopped. After the second pthread_create () call, both the main thread and thread B can assume that thread a exists (or has stopped ).

However, after the second create () call returns, the main thread cannot assume which thread (A or B) runs first. Although both threads already exist, the distribution of CPU time slices of threads depends on the kernel and the thread library. There are no strict rules for who will first run. Although thread a is more likely to start execution before thread B, this is not guaranteed. This is especially true for multi-processor systems. If the written code assumes that the code of thread a is actually executed before thread B starts to execute, the probability of the program running correctly is 99%. Or worse, the program runs correctly 100% on your machine, but the probability of running correctly on your customer's quad-processor server is zero.

The example also shows that the thread library retains the code execution sequence of each separate thread. In other words, the three pthread_create () calls are actually executed in the order they appear. From the perspective of the main thread, all code is executed in sequence. Sometimes, this can be used to optimize some thread programs. For example, in the preceding example, thread C can assume that thread a and thread B are either running or terminated. It does not have to worry about the possibility of creating threads A and B. This logic can be used to optimize the thread program.

5.2

Now let's look at another hypothetical example. Assume that many threads are executing the following code:

Myglobal = myglobal + 1;

So, do you need to lock and unlock mutex objects before and after the first operation? Some may say "no ". The compiler is very likely to compile the above assignment statement into a machine instruction. As we all know, it is impossible to "halfway" interrupt a machine command. Even hardware interruptions do not disrupt the integrity of machine commands. Based on the above considerations, it is likely that pthread_mutex_lock () and pthread_mutex_unlock () calls are omitted completely. Do not do this.

Am I talking nonsense? Not exactly. First, it should not be assumed that the above assignment statement will be compiled into a machine command unless the machine code is verified in person. Even if you insert some Embedded Assembly statements to ensure the complete execution of the add-on operation-or even write the compiler by yourself! -- Problems may still exist.

The answer is here. Using a single embedded assembly operation code on a single processor system may not cause any problems. Each plus one operation will be complete and most of the results will be expected. However, the multi-processor system is completely different. On multiple CPU machines, two separate Processors may execute the above assignment statement at almost the same time point (or at the same time point. Do not forget. In this case, the memory modification must first be written from L1 to L2 high-speed cache before being written to primary memory. (SMP machines are not just processors added; they also have special hardware used to handle Ram access .) In the end, it is impossible to find out which CPU will "win" in the competition for writing primary memory ". To generate predictable code, use mutex objects. A mutex will be inserted into a "Memory Level" to ensure that the write to the primary memory is performed in the order in which the thread locks the mutex object.

Consider an SMP architecture that updates the primary memory in 32-bit blocks. If you do not use a mutex, add one to a 64-bit integer. Up to four bytes of the integer may come from one CPU, while the other four bytes may come from another CPU. Bad! Worst of all, with poor technology, your program may not crash for a long time on the system of an important customer, that is, it crashes at three o'clock AM. In his POSIX thread programming book (see references at the end of this Article), David R. butenhof discusses the situations that will arise because mutex objects are not used.

6. Explanation of condition Variables

At the end of the previous article, I described a special problem: If the thread is waiting for a specific condition to happen, how should it handle this situation? It can repeatedly lock and unlock mutex objects. Each time it checks the shared data structure to find a value. However, this is a waste of time and resources, and the efficiency of such busy queries is very low. The best way to solve this problem is to use the pthread_cond_wait () call to wait for special conditions to occur.

 

6.1

Understanding the role of pthread_cond_wait () is very important-it is the core of the POSIX thread signal sending system and the most difficult to understand.

Call: pthread_cond_wait (& mycond, & mymutex );
Then, the pthread_cond_wait () call performs many operations before the return:
Pthread_mutex_unlock (& mymutex );
It unlocks mymutex and then enters sleep state, waiting for mycond to receive the POSIX thread "signal ". Once a "signal" is received (quotes are enclosed because we are not discussing traditional UNIX signals, but the signals from pthread_cond_signal () or pthread_cond_broadcast () calls), it will wake up. But pthread_cond_wait () does not return immediately -- it also needs to do one thing: Re-lock mutex:
Pthread_mutex_lock (& mymutex );
Pthread_cond_wait () knows the changes behind mymutex, so it continues to lock the mutex for us before returning.

 

Now we have reviewed the call of pthread_cond_wait (). you should understand how it works. All operations performed in sequence by pthread_cond_wait () should be described. Try it. If you understand pthread_cond_wait (), the rest is quite easy, so read the above again until you remember it. Okay. Can you tell me the status of the mutex object before calling pthread_cond_wait? After pthread_cond_wait () is returned, what is the status of the mutex object? The answer to both questions is "locked ". Now that you fully understand the pthread_cond_wait () call, let's continue to look at the simpler things-initialization and real sending and broadcasting processes. By that time, we will be familiar with the C code that contains the multi-threaded working queue.

6.2 initialize and clear
A condition variable is a real data structure to be initialized. The initialization method is as follows. First, define or assign a condition variable, as shown below:

Pthread_cond_t mycond;

Then, call the following function for initialization:

Pthread_cond_init (& mycond, null );

Look, Initialization is complete! You need to destroy a condition variable before releasing it, as shown below:

Pthread_cond_destroy (& mycond );

It's easy. Next we will discuss the call to pthread_cond_wait.

6.3 waiting
Once the mutex object and condition variable are initialized, you can wait for a condition as follows:

Pthread_cond_wait (& mycond, & mymutex );

Note that the Code should logically contain mycond and mymutex. A specific condition can only have one mutex object, and the condition variable should indicate a special condition change for the mutex data "internal. A mutex can use many condition variables (such as cond_empty, cond_full, and cond_cleanup), but each condition variable can only have one mutex object.

6.4 send signals and broadcast
Pay attention to sending signals and broadcasts. If the thread changes some shared data and wants to wake up all the waiting threads, use pthread_cond_broadcast to call it, as shown below:

Pthread_cond_broadcast (& mycond );

In some cases, the active thread only needs to wake up the first sleeping thread. Assume that you have added only one job to the queue. It is not polite to wake up another worker thread !) :
Pthread_cond_signal (& mycond );

This function only wakes up one thread. If POSIX thread standards allow you to specify an integer, you can wake up a certain number of sleeping threads, which is more perfect. Unfortunately, I was not invited to the meeting.

////////////////////////
Some code is as follows:
Http://www.faq-it.org/coldfusion/ "> faq-it.org/coldfusion/ void * Prin (void *)
{
Cout <"hello" <Endl;
}
 
 
Returnvalue = pthread_create (thread, null, Prin, null );
Compilation display: Undefined reference to pthread_create; why?
---------------------------------------------------------------
 
Add the parameter-lpthread.
 
Pthread library is not the default Library
 
G ++-lpthread-O my. O my. c
 
 
Good luck

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.