Objective
multithreading under a Linux system follows the POSIX threading interface, called Pthread. To write a multithreaded program under Linux, you need to use the header file Pthread.h, you need to use LIBPTHREAD.A when connecting. The implementation of Pthread under Linux is implemented by calling clone () from the system. Clone () is a Linux-specific system call that is used in a manner similar to fork.
Basic knowledge points and code implementations
1. Run multiple threads in a process, using the same address space with each other, sharing most of the data.
2. Starting a thread is much less than the amount of space that is spent starting a process.
3. The time required to switch between threads is much less than the time required to switch between processes.
4. Different processes have separate data spaces, and data transfer can only be done by means of communication. -time-consuming and inconvenient
shared data space between threads under the unified process, and one thread data can be used directly by other threads. -Fast and convenient
5. Places to note in writing Multithreading:
Some variables cannot be modified by two threads at the same time
Data declared as static in a subroutine can have a catastrophic blow to multithreading
6. Multithreading Advantages:
1) Improve application response, put time-consuming operations on a new thread, and avoid waiting.
2) make the CPU multi-core system more efficient.
3) Improve the program structure. A long, complex process can be divided into multiple threads.
7. A process is the basic unit of resource allocation, and threads have little resources. Sharing process Resources
8. The role of volatile is: as a directive keyword, ensure that this directive is not omitted due to compiler optimizations and requires each direct read value.
In short, it prevents the compiler from optimizing the code. For example, the following programs:
xbyte[2]=0x55; xbyte[2]=0x56; xbyte[2]=0x57; xbyte[2]=0x58;
For external hardware, the above four statements represent different operations, resulting in four different actions, but the compiler will optimize the above four statements, that only xbyte[2]=0x58 (that is, ignoring the first three statements, only one machine code). If you type volatile, the compiler compiles it one at a time and produces the corresponding machine code (producing four code).
9. Threading-related operations
9.1 identifier of the thread:p thread_t
pthread_t in header file/usr/include/bits/pthreadtypes.h is defined as follows:
typedef unsigned long int pthread_t;
9.2 Creating Threads: Pthread_creat
The prototypes are:
extern int pthread_create _p ((pthread_t *_thread,_const pthread_attr_t *_attr,void * (*_start_routine) (void *), void *_ ARG));
First parameter: Pointer to thread identifier second parameter: Set thread properties The third parameter: The starting address of the thread's running function The last parameter: The parameter of the Run function
Thread creation succeeded, returned 0, not 0, failed to create.
Common error return code: Eagain: System restriction creates a new thread EINVAL: invalid attribute value for thread
The thread is created successfully, and the newly created thread runs parameters three and parameter four to determine the function.
9.3 Waiting for thread to end Pthread_join and thread end Pthread_exit
pythread_join function Prototypes:
extern int Pthread_join_p ((pthread_t _th,void**_thread_return));
First parameter: The waiting thread identifier second parameter: a user-defined pointer that can be used to store the return value of the waiting thread
This function is a thread-blocking function, and the function that invokes it waits until the waiting thread ends, and when the function returns, the resource that is waiting for the thread is retracted.
There are two ways to end a thread: the end of the function, the end of the thread that invokes it, and the other through the function Pthread_exit.
pthread_exit Function Prototypes:
Exter void Pthread_exit_p ((void*_retval) _attribute_ ((_noreturn_));
only The parameter of one is the return code of the function: As long as the second parameter in Pthread_join Thread_return is not NULL, the value is passed to Thread_return.
A thread cannot be waited by more than one thread, otherwise the line Cheng of the first received signal is returned, and the remaining thread that calls Pthread_join returns the error code esrch.
10. Synchronization of Threads
while thread-local storage avoids threads accessing shared data, most of the data between threads is always shared. When it comes to reading and writing shared data, you have to use the synchronization mechanism, otherwise it will cause the threads to loot the results of shared data, which will make your data pieces confusing.
The thread synchronization mechanisms provided by Linux are mainly mutex and condition variables.
10.1 Mutual Exclusion Lock
The so-called mutual exclusion is that threads repel each other, and threads that get resources repel other threads that do not have access to resources. Linux uses mutexes to implement this mechanism.
Since the lock is called, there is the concept of locking and unlocking. When the thread is eligible for locking, it will enjoy the lock, and the other threads are "stunned" by the system as soon as they attempt to touch the lock. When the locking thread unlocks and discards the lock, those "stun" threads are awakened by the system and then continue to scramble for the lock. As for who can Rob, only God knows. But there's always one to grab. So the other to join the thread and the system to "stun" ... So repeated.
From this behavior of the mutex, the code between the line loads lock and the lock is equivalent to a single-plank bridge, agreeing that only one thread can execute at a time. Globally, in this place, all the threads running in parallel have become queued. The more professional term is synchronous execution, which is called the critical section of the code area. Synchronous execution breaks the thread parallelism, and the larger the critical section, the more destructive it is. Therefore, in practical application, we should try to avoid the presence of critical areas. No, the critical section should be as small as possible.
The interfaces that Linux initializes and destroys mutexes are Pthread_mutex_init () and Pthead_mutex_destroy (), with Pthread_mutex_lock (), Pthread_mutex_ for lock and unlock. Trylock () and Pthread_mutex_unlock (). The complete definition of these interfaces is as follows:
as you can see from these definitions, mutexes also have attributes. However, this property does not need to be changed in most cases, so the default property is used. The method is to pass NULL to it.
int Pthread_mutex_init (pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); int pthread_mutex_ Destory (pthread_mutex_t *mutex); int Pthread_mutex_lock (pthread_mutex_t *mutex); int Pthread_mutex_trylock (Pthread_ mutex_t *mutex); int Pthread_mutex_unlock (pthread_mutex_t *mutex);
Phtread_mutex_trylock () in particular, the thread that tries to lock is never "stunned" by the system, just by returning Ebusy to tell the programmer that the lock has already been used. It is up to the programmer to decide whether to continue the "breaking" critical section. The purpose of this interface is not to let the thread "break" the critical section. Its fundamental purpose is to improve parallelism by keeping this thread to do something else that is meaningful. Of course, if it is fortunate that no one has the lock at this time, then nature will have access to the critical section.
The mutex is within the same thread and has no mutually exclusive attributes. In other words, a thread cannot use a mutex to let the system "stun" itself. A good reason to explain this is that the thread that owns the lock is "dizzy" and who can have the lock again? But other things need to be avoided, that is, two threads have their own lock, but also want to get the other's lock, this time two threads will be "stun." Once this happens, no one can get the lock, and there is a famous name-the deadlock. A deadlock is something that will always be avoided because it is a serious act of not being selfish.
10.2 Item Variables
The condition variable key is on the variable. Unlike locks, when a thread encounters this "variable", it is not like a lock that is "stunned" by the system, but chooses whether to wait there, depending on the condition. Waiting for what? Wait for the "signal" that is allowed to pass. Is this "signal" controlled by the system? Obviously not! It is controlled by another thread.
A condition variable is an event mechanism. The "event" is controlled by a class of threads, and another class of threads waits for an "event" to occur. In order to implement this mechanism, the condition variable must be a global variable shared between the threads. Also, the condition variable needs to be used in conjunction with the mutex.
the interfaces that initialize and destroy the condition variables are pthread_cond_init () and Pthread_cond_destory (); The interface that controls the event occurs is pthread_cond_signal () or Pthread_cond_ Broadcast (); The interface that waits for an "event" to occur is pthead_cond_wait () or pthread_cond_timedwait (). Their complete definition is as follows:
int Pthread_cond_init (pthread_cond_t *cond,const pthread_condattr_t *attr); int pthread_cond_destory (pthread_cond_t * cond); int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait (pthread_cond_t *cond,pthread_mutex_t *mutex,const timespec *abstime); int pthread_cond_signal (pthread_cond_t *cond); int Pthread_cond _broadcast (pthread_cond_t *cond);
for an interface waiting for an "event" it can be seen from its name that one waits indefinitely and one is a time-limited wait. The latter is similar to the Pthread_mutex_trylock () of the mutex, that is, when the waiting "event" has not happened after a period of time, then do something meaningful. For the interface that controls "event", there are "unicast" and "broadcast". Unicast means that only one thread gets the "notification" that the "event" has already occurred, and the broadcast is that all threads are "notified". For broadcast situations, all threads that are "notified" are also going through a bridge controlled by a mutex.
Here is an example of a simple multithreading :#include to include the header file Pthread.h also need to link libpthread.so library, link phase should have similar directive: GCC program.o-o program-lpthread
Multithreading Examples:
#include <stdio.h> #include <pthread.h>void *thread (void *arg) {printf ("This is a thread and arg=%d.\n", * (int *) arg); * (int*) arg = 0; return ARG;} int main (int argc,char *argv[]) {pthread_t th; int ret; int arg = ten; int *thread_ret = NULL; ret = Pthread_create (&th,null,thread,&arg); if (ret!=0) { printf ("Create thread error!\n"); return-1;} [Email protected]:~$ gcc thread.c-o thread-lpthread[email protected]:~$./threadthis is the main process. This is a thread and arg=10.thread_ret=0.
11. Merging and separating threads
The merging of threads is a matter of reclaiming thread resources to avoid resource leaks.
When a process or thread invokes the Pthread_join () interface for another thread, the thread is merged. This interface blocks the calling process or thread until the merged thread ends.
When the merge thread ends, the Pthread_join () interface reclaims the thread's resources and returns the return value of the thread to the Consolidator.
Another thread-recycling mechanism that corresponds to thread merging is thread separation, which is the Pthread_detach () of the calling interface. Thread separation is the process of recycling thread resources to the system automatically, which means that the system automatically reclaims its resources when the disconnected thread ends. Because thread separation is the automatic recovery mechanism of the boot system, the program will not be able to get the return value of the separated thread, which makes the Pthread_detach () interface have a single parameter, which is the split thread handle.
Thread merging and thread separation are all used to reclaim thread resources and can be used as appropriate for different business scenarios. Whatever the reason, you have to choose one of them, or you will cause a resource leak, which is just as scary as a memory leak.
Two examples and implementations of multi-threading programming
12.1) Linux is written as a multi-line program that outputs each thread number.
Design ideas: Today saw the teacher's program, understand about the thread number output function pthread_self (). Very simple, I mainly the main thread and the sub-thread number of the multithreaded program output. Encountered a problem in the process, the main thread number output, the child thread number is not output. Think carefully, the main thread is finished, the sub-thread has not been executed before the end. What do we do? The Sleep function, which allows the main thread to wait for 10s, eventually achieves its intended purpose.
The procedure is as follows:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h>void * Thread (void *arg) { printf ("Thread ID is%lu.\n", pthread_self ()); return NULL;} int main () {pthread_t id;printf ("Main thread ID is%lu \ n", pthread_self ()); if (!pthread_create (&id,null, (void *) Thread,null) {printf ("succeed!\n"); sleep (); return 0;} else{printf ("Fail to Create Thread"); return-1;}}
Operation Result:
Note: Compile the time to link library: Gcc-o thread1 thread1.c-lpthread
12.2) Use multithreading to calculate PI values.
Design ideas: Here we use the median integral theorem to calculate the π value. My idea is to divide the execution of the thread into four segments, each thread to execute a section, the problem is that the thread passed the parameters, some did not calculate, too late to pass the past, the end of the thread, I would like to let each thread create, wait for 1s.
The procedure is as follows:
#include <stdio.h> #include <pthread.h> static long num_steps=100000; const int numthreads = 4; Double step, pi; pthread_mutex_t Mut; Double sum = 0.0; void * thread (void *parg) {double x; int i; int temp = * ((int *) PARG); int start = temp* (NUM_STEPS/4); int end = start + NUM_STEPS/4; printf ("%d%d%d\n", temp,start,end); The test procedure can be removed for (I=start; i<end; i++) {pthread_mutex_lock (&mut); x = (i+0.5) *step; sum = sum + 4.0/(1.0 + x*x); Pthread_mutex_unlock (&mut); } return 0; } void Main () {int i; pthread_t Hthread[numthreads]; Pthread_mutex_init (&mut,null); Step = 1.0/(double) num_steps; for (i=0;i<numthreads;i++) {Hthread[i] = Pthread_create (&hthread[i], null,thread, &i); Sleep (1); }//For (i=0;i<numthreads;i++) {//Pthread_join (Hthread[i],null); } PI = step * sum; printf ("PI =%12.9f\n", pi); }
Operation Result:
Linux multithreaded Programming