Linux multi-threaded programming API and steps

Thread overview

Process is the basic unit of program execution and resource allocation in the system.
Each process has its own data segment, code segment, and stack segment, which causes the process to have more responsible context switching and other actions when switching and other operations. In order to further reduce the idle time of the processor to support multi-processors and reduce the overhead of context switching, another concept-thread appears in the process of evolution. It is the basic scheduling unit within a process, and can also be called a lightweight process.
Threads are concurrent multiple execution paths in a shared memory space. They share the resources of a process, such as file description and signal processing. Therefore, the overhead of context switching is greatly reduced.

Like the process, the thread also places related variable values ​​in the TCB in the thread control table.
A process can have multiple threads, that is, multiple thread control tables and stack registers, but share a user address space.
It should be noted that because threads share the resources and address space of the process, any thread's operation on system resources will affect other threads. Therefore, synchronization in multiple threads is a very important issue.
In a multithreaded system, the relationship between processes and threads is shown in Table 8.1.

Insert picture description here

Linux thread implementation

1. Thread creation and exit

(1) Function description
Thread creation: pthread_create.
Thread exit:
(1) After the thread is created, it starts to run the related thread function. After the function is finished, the thread exits. This is also a method for thread exit.
(2) Another way to exit a thread is to use the function pthread_exit. Since the function of exit is to terminate the calling process, a process often contains multiple threads. Therefore, after exit is used, all threads in the process are terminated. Therefore, pthread_exit can be used in the thread to replace the exit in the process.
After the thread exits, the resources occupied by the exiting thread will not be released with the termination of the thread.
pthread_join() function. pthread_join can be used to suspend the current thread and wait for the end of the thread. This function is a thread blocking function. The function that calls it will wait until the waiting thread ends. When the function returns, the resources of the waiting thread will be recovered.

Insert picture description here
*如:pthread_create( &id3,NULL, (void )thread3,&tmp );

Insert picture description here
Insert picture description here
In the following example, two threads are created. The first thread calls the pthread_exit function to exit when the program runs halfway, and the second thread exits normally. Collect the exit information of these two threads in the main thread and release resources. As can be seen from this example, these two threads are running concurrently.
gcc -o th th.c -lpthread (the thread library should be added when compiling)

Because threads share the resources and address space of the process, when operating on these resources, the uniqueness of resource access between threads must be considered. Here we mainly introduce the method of thread synchronization in POSIX, mainly including mutexes and semaphores. the way.

2. Mutex mutex lock thread control (1) function description

Mutex is a simple method of locking to control access to shared resources.
This mutex has only two states, that is, locked and unlocked.
The mutex can be regarded as a global variable in a sense. At the same time, only one thread can master the lock on a mutex, and the thread with the locked state can operate on shared resources. If other threads want to lock an already locked mutex, the thread will be suspended until the locked thread releases the mutex. It can be said that this mutex lock allows shared resources to operate in each thread in order.

Mutex initialization: pthread_mutex_init
mutex lock: pthread_mutex_lock—blocking
mutex lock judgment lock: pthread_mutex_trylock—non-blocking (if it has been locked, return EBUSY; if it is not locked, lock it, success is 0 , Unsuccessful for other values)
mutex unlock: pthread_mutex_unlock
eliminate mutex: pthread_mutex_destroy

Mutex locks can be divided into:
fast lock: refers to the calling thread will block until the thread that owns the mutex lock is unlocked.
Recursive mutex: can successfully return and increase the number of times the calling thread locks on the mutex.
Error detection mutex: it is a non-blocking version of the fast mutex, it will return immediately and return an error message.

Insert picture description here

Insert picture description here

3. Semaphore thread control (1) Semaphore description

Semaphore is the PV primitive used in the operating system. It is widely used for synchronization and mutual exclusion between processes or threads. It is essentially a non-negative integer counter.
The PV primitive is an operation on the integer counter semaphore sem. A P operation decreases sem by one (to allocate resources), and a V operation increases sem by one (to release resources).
Processes (or threads) determine whether they have access to public resources based on the value of the semaphore. When the value of the semaphore sem is greater than or equal to 0, the process (or thread) has access to public resources; on the contrary, when the value of the semaphore sem is less than zero, the process (or thread) will block until the semaphore sem The value of is greater than or equal to 0.
PV primitives are mainly used in two typical situations of synchronization and mutual exclusion between processes or threads. If used for mutual exclusion, several processes (or threads) often only set one semaphore sem, and their operation flow is shown in Figure 8.2.
When the semaphore is used for synchronous operation, multiple semaphores are often set, and different initial values ​​are arranged to realize the sequential execution among them. Their operation flow is shown in Figure 8.3.

Insert picture description here
Insert picture description here

sem_init is used to create a semaphore and initialize its value.
sem_wait and sem_trywait are equivalent to P operations. They can both reduce the value of the semaphore by one. The difference between the two is that if the semaphore is less than 0, sem_wait will block the process, and sem_trywait will return immediately.
sem_post is equivalent to the V operation, which adds one to the value of the semaphore and sends a signal to wake up the waiting process.
sem_getvalue is used to get the value of the semaphore.
sem_destroy is used to delete semaphores.

Insert picture description here
Insert picture description here

Semaphore lock example:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>


int lock_var;
time_t end_time;
sem_t sem1,sem2,sem3;

void pthread1(void *arg);
void pthread2(void *arg);
void pthread3(void *arg);


int main(int argc, char *argv[])
{
    
    
	pthread_t id1,id2,id3;
	pthread_t mon_th_id;
	int ret;
	end_time = time(NULL)+30;
	ret=sem_init(&sem1,0,1);
	ret=sem_init(&sem2,0,0);
	ret=sem_init(&sem3,0,0);
	if(ret!=0)
	{
    
    
		perror("sem_init");
	}
	ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);
	if(ret!=0)
		perror("pthread cread1");
	ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);
	if(ret!=0)
		perror("pthread cread2");

	ret=pthread_create(&id3,NULL,(void *)pthread3, NULL);
	if(ret!=0)
		perror("pthread cread2");
	
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);
	pthread_join(id3,NULL);
	exit(0);
}

void pthread1(void *arg)
{
    
    
	int i;
	while(time(NULL) < end_time){
    
    
		sem_wait(&sem1);
		for(i=0;i<2;i++){
    
    
			sleep(1);
			lock_var++;
			printf("pthread1----lock_var=%d\n",lock_var);
		}
		printf("pthread1:lock_var=%d\n",lock_var);
		sem_post(&sem2);
		sleep(1);
	}
}

void pthread2(void *arg)
{
    
    
	int nolock=0;
	int ret;
	while(time(NULL) < end_time){
    
    
	sem_wait(&sem2);
	printf("pthread2:pthread2 got lock;lock_var=%d\n",lock_var);
	sem_post(&sem3);
	sleep(3);
	}
}

void pthread3(void *arg)
{
    
    
	int nolock=0;
	int ret;
	while(time(NULL) < end_time){
    
    
	sem_wait(&sem3);
	printf("pthread3:pthread3 got lock;lock_var=%d\n",lock_var);
	sem_post(&sem1);
	sleep(3);
	}
}

Guess you like

Origin blog.csdn.net/hhltaishuai/article/details/107719726