Linux C system programming (10) basic operation of thread management

1 Thread overview

1.1 Introduction to threads

In the Linux environment, the so-called thread is a lightweight process. The operating system allocates resources in units of processes and uses multiple small processes to complete different tasks concurrently in an execution space. Such small processes are called threads. In the thread of the same process, there are shared resources and private resources, as follows:

  1. Shared resources: address space, global variables, open files, child processes, alarms, signals and signal service programs, accounting information
  2. Private resources: program counters, registers, stacks, status words.

1.2 Thread implementation model of modern operating system

The threading model of modern operating systems is based on the previous user-mode threading model and kernel-mode threading model, or a combination of the two. The user mode execution system is responsible for the switching of internal threads in the process when it is not blocked; the kernel mode operating system is responsible for the switching of blocked threads. There are fewer kernel-mode threads and more user-mode threads. Each kernel-mode thread serves one or more user-mode threads. When allocating threads, generally set the threads that need to perform blocking operations as kernel mode threads, and set the threads that will not perform blocking operations as user mode threads.

1.3 Advantages of multithreading

Increase the concurrency of the program, thereby improving the operating efficiency of the program.

1.4 Thread programming needs to pay attention to

  1. This set of pthread functions is useless with the man manual, because it is a separate pthread library.
  2. The pthread related functions are in the header file pthread.h.
  3. Generally, errors will occur during direct compilation. You must add the library -lpthread.    

2 Thread identifier

Each thread has its own ID, which is represented by the data type pthread_t, which is essentially an unsigned integer, that is, typedef unsigned int pthread_t; for portability reasons, the pthread_t type cannot be equivalent to unsigned integer Type operation.

2.1 pthread_self function

Use pthread_self function to get the ID of a thread under Linux

pthread_t pthread_self(void);
函数返回本线程的线程ID。

2.2 pthread_equal function

Use pthread_equal function to compare whether two thread IDs are the same under Linux

int pthread_equal(pthread_t tid1,pthread_t tid2);
参数tid1\tid2:表示将要比较的量个线程ID号。
如果两个值相同,返回0,不相等则返回非零值。

3 Thread creation pthread_creat function

Use pthread_creat function to create a thread under Linux:

int pthread_create (pthread_t * restrict tidp , pthread_attr_t *restrict \
attr,void *(*start_routine) (void *),void *restrict arg);

参数tidp:pthread_t类型的指针,一个值结果参数。
参数attr:线程的属性,不想指定特定属性的时候置为NULL。
参数start_rtn:一个函数指针(返回值是一个指向void型的指针,参数也是一个指向void型的指针),创建的线程要从该函数的起始位置处开始执行,函数返回该线程就停止了。
参数arg:一个void型的指针,是函数start_rtn的参数,在线程开始执行时,该参数由内核传递给线程。
函数执行成功返回0,失败返回错误编号。这种规律适合于所有的线程系列函数。

note:

  1. Threads and processes under Linux have equal scheduling rights. The scheduling order of one thread and another thread is completely unpredictable, which depends on the kernel scheduling algorithm.
  2. If you want to pass multiple parameters to the pthread_creat function, you need to organize all the parameters in a structure, and then pass the address of the structure as a parameter to the new thread.
  3. Thread series functions generally do not set the value of errno, but return error number. Because threads can freely access the environment variables of the process, when multiple threads make mistakes, the value of errno will be overwritten multiple times, and the process can only check the cause of the last error thread.         

4 Thread termination

4.1 There are 3 ways to terminate the thread

  1. Thread function execution ends: use the thread created by pthread_creat to execute a function, and exit the thread if the function execution ends. Similar to the return of the main function.
  2. Canceled by another thread: similar to being killed between processes.
  3. The thread exits on its own: similar to calling the exit function.

4.2 pthread_exit function

Use pthread_exit function to terminate the thread under Linux:

void pthread_exit(void *rval_ptr);
参数rval_ptr:一个指向void型的指针,该指针指向的区域存储退出信息,该信息类似于传递给新线程的参数,将多个信息组织成一个结构体。
函数无返回值。

There are two types of end information for a thread:

  1. The area pointed to by the pointer returned by the thread body function. Get the return value of the thread body function.
  2. The area pointed to by the pthread_exit function. Get the exit information set by the pthread_exit function.

4.3 pthread_join function

Use pthread_join to access the end information of a specified thread under Linux:

int pthread_join(pthread_t tid,void **rval_ptr);
参数tid:需要取得结束信息的线程。(如果该线程正在运行,则等待,直到该线程结束执行为止;如果指定线程的ID和调用线程的ID不属于同一进程,则出错)。
参数rval_ptr:一个指向void型的指针(因为要在内核中改变它的值)
//如果线程由于线程体函数返回/调用pthread_exit函数退出,则参数指向的是退出信息的首地址。
//如果其他线程由于被其他线程取消而退出则该参数被置为PTHREAD_CANCELED常量。
//如果不关心返回值,则将参数置为NULL。这时仅仅等待线程执行结束,不获得线程退出信息。
函数执行成功返回0,失败返回错误号。

The general thread usage model is:

pthread_create();          //创建第1个线程
pthread_create();          //创建第2个线程
...
pthread_create();          //创建第n个线程
...
...
pthread_join();          //得到第1个线程的退出信息
pthread_join();          //得到第2个线程的退出信息
...
pthread_join();          //得到第n个线程的退出信息

4.4 How to exit the message correctly

After the thread finishes running, the Linux kernel only saves the first address of the memory area where the exit information is stored, and does not store the exit information in the kernel. Generally, dynamically allocated memory / global variables are used to save the thread exit information.

4.5 Cancel the running of a thread pthread_cancel

One thread can be canceled by another thread. Use pthread_cancel function to cancel another thread under Linux:

int pthread_cancel(pthread_t tid);
参数tid:要取消线程的线程ID。
函数执行成功返回0,失败返回错误编号。

Calling the pthread_cancel function is equivalent to calling the cancelled thread itself: pthread_exit (PTHREAD_CANCELED);

4.6 Thread cleaning function pthread_cleanup_push / pthread_cleanup_pop

Use pthread_cleanup_push / pthread_cleanup_pop to achieve the end of thread cleaning under Linux:

void pthread_cleanup_push(void (*rtn)(void *),void *arg);          //设置清理程序    
void pthread_cleanup_pop(int execute);                              //执行清理程序
参数rtn: 处理程序入口地址。
参数arg: 传递给处理函数的参数。
参数execute:
0:不执行清理程序。但是将栈顶的清理程序记录出栈。
非0:执行栈顶清理程序。执行后栈顶的清理程序记录出栈。    
函数无返回值。

Note: The pthread_cleanup_push function will be executed in 3 situations:

  1. Call the pthread_exit function.
  2. Canceled by other threads.
  3. When calling the pthread_cleanup_pop function with a non-zero value parameter.

pthread_push and pthread_pop should be used in pairs, otherwise an error will occur. Because pthread_cleanup_push () and pthread_cleanup_pop () are implemented in macro mode, this is the macro definition in pthread.h:

#define pthread_cleanup_push(routine,arg) \
{
     struct _pthread_cleanup_buffer _buffer; \
     _pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
     _pthread_cleanup_pop (&_buffer, (execute)); \
    }

It can be seen that pthread_cleanup_push () carries a "{", while pthread_cleanup_pop () carries a "}". The order of setting the cleaning procedure and executing the cleaning procedure is the reverse.

4.7 Thread separation pthread_detach function

Use pthread_detach function under Linux to achieve dynamic separation of threads:

int pthread_detach(pthread tid);
参数tid:要使之处于分离状态的线程ID。

This function is to set the status of the child thread to detached, then all resources will be automatically released after the thread runs.


5 Thread synchronization_mutexes pthread_mutex _ ***

A mutex is a kind of lock, which is added when accessing a shared resource and released at the end, so that only one thread is in the critical zone at any time. Anyone who wants to enter the critical section will test the lock. If the lock is already held by a thread, the test thread will be blocked until the lock is released, and the thread repeats the above process.

5.1 Initializing and destroying the mutex

In the Linux environment, the pthread_mutex_t data type is used to represent the mutex, and it needs to be initialized when it is used. When not in use, destroy them.

pthread_mutex_init:锁的初始化
int pthread_mutex_init(pthread_mutex_t *mutex, const
pthread_mutexattr_t *mutexattr)
pthread_mutex_destroy:锁的销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数mutex:互斥锁的指针,一种特殊的类型参数。
参数mutexattr:锁的属性,缺省值为NULL。
函数执行成功返回0,失败返回错误号。

There are two ways to create a mutex: static and dynamic. POSIX defines a macro PTHREAD_MUTEX_INITIALIZER to statically initialize the mutex, as follows:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
/*(在 LinuxThreads 实 现 中 , pthread_mutex_t 是 一 个 结 构 ,\
 而PTHREAD_MUTEX_INITIALIZER 则是一个结构常量)*/

The dynamic way is to use the pthread_mutex_init () function to initialize the mutex.The API is defined as follows:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 

Mutexattr is used to specify the attribute of the mutex. If it is NULL, the default attribute is used.

5.2 Obtaining and releasing the mutex

Lock it when in use. Unlock the lock at the end of its operation. The functions involved are:

//pthread_mutex_lock:加锁操作
int pthread_mutex_lock(pthread_mutex_t *mutex);
//pthread_mutex_trylock:加锁操作
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//pthread_mutex_unlock:解锁操作
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数mutex:互斥锁的指针,一种特殊的类型参数。
函数执行成功返回0,失败返回错误号。

The difference between pthread_mutex_lock and pthread_mutex_trylock functions:

  1. If the lock requested by the former is acquired by a thread, it will block until the mutex is released.
  2. If the lock requested by the latter is acquired by a thread, it will not block, and immediately returns an error number EBUSY, indicating that the applied lock is busy.

6 Thread synchronization_read-write lock pthread_rwlock _ ***

A mutex lock has only one thread at a time to operate the lock, and other threads are blocked because they cannot obtain the lock. The original intention of creating multi-threaded operation is to execute tasks concurrently, but due to the mutex lock, the operation of the thread becomes serial, and the efficiency of the program will be reduced. During program execution, if there are far more threads reading a shared resource than writing threads, the use of such read-write locks can greatly increase the concurrency of the threads, thereby improving the operating efficiency of the threads.

6.1 Initializing and destroying read-write locks

The linux environment uses the pthread_rwlock_t data type to indicate the type of read-write lock, which needs to be initialized when it is used. When not in use, destroy them.

//pthread_rwlock_init:初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);  
//pthread_rwlock_destroy:销毁读写锁  
int pthread_rwlock_destroy(pthread_rwlock_t *restrict rwlock);
参数rwlock:读写锁的指针。
参数attr:读写锁的属性,缺省值为NULL。
函数执行成功返回0,失败返回错误号。

6.2 Obtaining and releasing read-write lock

//pthread_rwlock_rdlock:设定读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);      
//pthread_rwlock_wrlock:设定写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);   
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。

The implementation may limit the number of locks in the read mode of the read-write lock, so we need to check the return value to determine whether it is successful. The other two functions will return an error, but as long as our lock design is appropriate, we can not check. The non-blocking functions are:

//pthread_rwlock_rdlock:设定读锁    
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);  
//pthread_rwlock_wrlock:设定写锁    
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。
//当锁成功获取时,返回0,否则返回EBUSY。
//这两个函数可以避免死锁。如果针对未初始化的读写锁调用进行读写操作,则结果是不确定的。

pthread_rwlock_unlock: release the read-write lock (used to release the lock held in the read-write lock object referenced by rwlock.)

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);    
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。

7 Thread synchronization_condition variable pthread_cond _ ***

7.1 Initialization and destruction of condition variables

The data type of the condition variable is pthread_cond_t, and it must be initialized before use.

//pthread_cond_init:条件变量初始化
int pthread_cond_init(pthread_cond_t *restrict cond,pthread_condattr_t *restrict attr);
//pthread_cond_destroy:条件变量销毁
int pthread_cond_destroy(pthread_cond_t *cond);
参数cond:条件变量,一个特殊的参数类型。
参数attr:条件变量的属性,缺省值为NULL。    
函数执行成功则返回0, 出错则返回错误编号。

Two ways to initialize condition variables:

  1. Static: The constant PTHREAD_COND_INITIALIZER can be assigned to a statically assigned condition variable.
  2. Dynamic: The pthread_cond_init function is to clear the memory space of the dynamic condition variable with pthread_cond_destroy before releasing it.

7.2 Waiting conditions for condition variables

The wait condition function waits for the condition to become true. The mutex passed to pthread_cond_wait protects the condition. The caller passes the locked mutex to the function. The function puts the calling thread on the waiting list of threads and then unlocks the mutex. These two operations are atomic. This closes the time channel between condition checking and the thread goes to sleep and waits for the condition to change, so that the thread will not miss any changes in the condition. When pthread_cond_wait returns, the mutex is locked again.

//pthread_cond_wait:阻塞等待
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);
//pthread_cond_timewait:超时等待
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);
参数cond:条件变量,一个特殊的参数类型。
参数mutex:等待的指定的锁,一般在其他线程内。
参数timeout:等待的时间。
函数执行成功则返回0, 出错则返回错误编号。

The difference between the two functions: the two work in a similar way, except that the pthread_cond_timedwait function has an extra timeout that specifies the timeout period.

7.3 Notification conditions for condition variables

The following two functions are used to notify the thread that the condition has been met. Calling these two functions is also called sending a signal to the thread or condition. It must be noted that the signal must be sent to the thread after changing the condition state.

pthread_cond_signal:
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_broadcast:
int pthread_cond_broadcast(pthread_cond_t *cond);
参数cond:条件变量,一个特殊的参数类型。
成功则返回0, 出错则返回错误编号。

The difference between the two functions:

  1. pthread_cond_signal is to wake up a thread waiting for this condition.
  2. pthread_cond_broadcast is to wake up all threads waiting for this condition.
     
Published 289 original articles · praised 47 · 30,000+ views

Guess you like

Origin blog.csdn.net/vviccc/article/details/105165155