“This is the second day of my participation in the First Challenge 2022.

1. Introduction

The previous article introduced the Linux process creation, management, use, communication, understand the multi-process concurrency; This article introduces the basic use of threads under Linux.

The difference between thread and process (1) Process: is the smallest unit of operating system scheduling. In Linux, you can run the ps and top commands to view details about processes. (2) Thread: it is the smallest unit of process scheduling. Each process has a main thread. The main thing you do in a process is threads.

(3) In the whole system, the process ID is the unique identifier, and the process management is realized by PID. Each time a process is created, the kernel creates a structure to store all the process information, and each node that stores the process information also keeps its own PID. This ID is used when you need to manage the process (such as sending signals). When the child process is finished and needs to recycle (exit() or the code is finished), wait() system call is required. The dead process that is not collected will become a zombie process. Its process entity no longer exists, but it will empty PID resources, so recycling is necessary.

For threads, active termination requires a call to pthread_exit(), and the main thread needs to call pthread_join() to reclaim (if the thread has not set the “separation attribute”). The image line also sends thread signals through thread IDS

Communication between processes: A. Shared memory B. message queue C. Semaphore D. Named pipes E. Nameless pipes F. Signals G. Files H. Mutex B. spin lock C. condition variable D. read/write lock e. thread signal F. The global variable

Communication between processes requires either switching kernel contexts or accessing peripherals (named pipes, files). So it’s going to be slower. However, if the thread adopts its own unique communication mode, it will basically complete in its own process space without switching, so the communication speed will be faster. In other words, the communication methods used between processes and threads differ not only in kind but also in speed.

Note: When a process running multiple threads catches a signal, it blocks only the main thread, and other child threads continue to execute.

2. Introduction to thread-related functions

2.1 Creating a Thread

Pthread_create is a thread-creating function for Unix operating systems (Unix, Linux, etc.). The link library needs to be specified at compile time: -lpthread function prototype

#include <pthread.h>
int pthread_create
(
pthread_t *thread, 
const pthread_attr_t *attr,
void *(*start_routine) (void *), 
void *arg
);
Copy the code

Parameter is introduced

The first argument is a pointer to the thread identifier. The second parameter sets the thread properties. NULL can be specified by default. The third argument is the starting address from which the thread runs the function. The last argument is the argument to run the function. NULL is not required. # man pthread_create

Return value: 0 if the thread was created successfully. If the thread creation fails, the error number is returned. After a thread is successfully created, the attr parameter is used to specify various thread properties. The newly created thread starts at the address of the start_rtn function, which has only one universal pointer argument, arg. If you need to pass more than one argument to the thread worker function, you need to put those arguments into a structure and pass the address of that structure as an argument to the ARG.

Example:

// Thread function 1 void *pthread_func1(void *arg) {while(1) {printf(" Thread function 1 is running..... \n"); sleep(2); // Thread function 2 void *pthread_func2(void *arg) {while(1) {printf(" thread function 2 is running..... \n"); sleep(2); } } int main(int argc,char **argv) { pthread_t thread_id1; pthread_t thread_id2; If (pthread_create(&thread_id1,NULL,pthread_func1,NULL)) {printf(" thread 1 failed to create! \n"); return -1; If (pthread_create(&thread_id2,NULL,pthread_func2,NULL)) {printf(" Thread 2 failed to create! \n"); return -1; */ pthread_join(thread_id1,NULL); pthread_join(thread_id2,NULL); return 0; } //gcc pthread_demo_code.c -lpthreadCopy the code

2.2 Exiting a Thread

The thread terminates execution by calling the pthread_exit function, just as the process calls exit when it terminates. This function terminates the calling thread and returns a pointer to an object.

This function terminates the calling thread and returns a pointer to an object that can be retrieved from the second argument to the pthread_join function.

The function prototype

#include <pthread.h>
void pthread_exit(void *retval);
Copy the code

Parameter specifies the address that the parsing thread needs to return. Note: Thread terminations must release the thread stack, that is, the thread function must call pthread_exit() to terminate, otherwise it will not be released until the main process function exits

2.3 Waiting for a Thread to End

The pthread_join() function blocks until the thread specified by thread terminates. When the function returns, the resource held by the waiting thread is retrieved. If the thread is terminated, the function returns immediately. And the thread specified by Thread must be a Joinable property. The function prototype

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Copy the code

Parameter The first parameter: the thread identifier, the thread ID, identifies a unique thread. The last parameter: a user-defined pointer to the address to be returned by the waiting thread. The return value 0 represents success. Failure, an error number is returned. Example value returned by the receiving thread:

Pthread_exit (" Thread exits normally "); // The return value of the receiving thread void *pth_join_ret1; pthread_join( thread1, &pth_join_ret1);Copy the code

2.4 Thread separation properties

The default state for creating a thread is Joinable. If a thread terminates without calling pthread_JOIN, it is ina Zombie Process state, i.e. some resources have not been collected (exit status code). So the thread creator should pthread_JOIN to wait for the thread to finish running and retrieve the thread’s exit code to reclaim its resources (similar to wait, waitPID for a process). However, after calling pthread_join(pthread_id), if the thread is not finished running, the caller will be blocked, which is not desirable in some cases.

The pthread_detach function can set the state of the thread to detached, so that all resources will be freed automatically when the thread finishes running. The function prototype

#include <pthread.h>
int pthread_detach(pthread_t thread);
Copy the code

The parameter thread identifier returns a value of 0 indicating success. Error return error code. The EINVAL thread is not a joinable thread. ESRCH has no thread ID to be found.

2.5 Obtaining the identifier of the current thread

The pthread_self function obtains the ID of the thread itself. The function prototype

#include <pthread.h>
pthread_t pthread_self(void);
Copy the code

Return value The identifier of the current thread. Pthread_t is of type unsigned long int, so use %lu when printing otherwise it will display wrong.

2.6 Automatically Clearing Thread Resources

A thread can schedule functions that it needs to call when it exits. Such functions are called thread cleanup handlers. Used to clean up resources when the program exits unexpectedly. A pthread_cleanup_push()/ pthread_cleanup_POP () function is provided in the POSIX threading API to automatically release resources. Termination actions (including calls) in the program segment between the call point of pthread_cleanup_push() and pthread_cleanup_POP () Pthread_exit () and exception termination) will both execute the cleanup function specified by pthread_cleanup_push().

Note: The pthread_cleanup_push and pthread_cleanup_POP functions need to be called in pairs. The function prototype

void pthread_cleanup_push(void (*routine)(void *),void *arg); Void pthread_cleanup_pop(int execute); // Release the cleanup functionCopy the code

Void (*routine)(void *) : the function entry to the handler. Void *arg: parameter passed to the handler function. Int execute: indicates the execution status. 0 indicates that the cleanup function is not called. 1 indicates that the cleanup function is invoked.

Conditions that cause a clean function call:

  1. Call the pthread_exit() function
  2. Pthread_cleanup_pop takes a parameter of 1. Note: Return does not cause the function call to be cleaned up.

2.7 Automatic thread cleanup sample code

Void routine_func(void *arg) {#include <stdio.h> #include <pthread.h> Printf (" Thread resource cleared successfully \n"); } // the thread worker function void *start_routine(void *dev) {pthread_cleanup_push(routine_func,NULL); // pthread_exit(NULL); pthread_cleanup_pop(1); //1 causes the cleanup function to be called. 0 will not be called. } int main(int argc,char *argv[]) { pthread_t thread_id; If (pthread_create(&thread_id,NULL,start_routine,NULL)! =0) {printf(" Thread creation failed! \n"); If (pthread_detach(thread_id)! =0) {printf(" Separation property setting failed! \n"); } while(1){} return 0; }Copy the code

2.8 Thread cancellation function

The pthread_cancel function is a thread cancellation function used to cancel other threads in the same process.

Pthread_cancel (pthread_t tid);Copy the code