Multithreading Foundation

Thread overview

Thread is a lightweight process (LWP). In Linux environment, the essence of thread is still a process. The program running on the computer is a combination of a group of instructions and instruction parameters. The instructions control the computer according to the established logic. The operating system will allocate system resources in the unit of process. It can be understood that process is the smallest unit of resource allocation, and thread is the smallest unit of scheduling and execution of the operating system.

Let's first understand the conceptual differences between threads and processes:

  • The process has its own independent address space, and multiple threads share the same address space
    Threads save more system resources, and the efficiency can not only be maintained, but also be higher
    In one address space, multiple threads are exclusive: each thread has its own stack area and register (managed in the kernel)
    In an address space, multiple threads share code segments, heap areas, global data areas, and open files (file descriptor tables)

  • Thread is the smallest execution unit of a program, and process is the smallest resource allocation unit in the operating system
    Each process corresponds to a virtual address space. A process can only grab one CPU time slice
    One address space can be divided into multiple threads, which can grab more CPU time slices on the basis of effective resources

  • CPU scheduling and switching: context switching of threads is much faster than that of processes
    Context switching: the process / thread time-sharing reuses the CPU time slice. Before switching, the state of the previous task will be saved. The next time it switches back to this task, the state will continue to run. The process from saving to reloading the task is a context switching.

  • Threads are cheaper, start faster, exit faster, and have less impact on system resources.

When dealing with multitasking programs, using multithreading has more advantages than using multiple processes, but the more threads are not the better. How to control the number of threads?

File IO operation: file IO has low CPU utilization, so CPU time slices can be reused time-sharing. The number of threads = 2 * the number of CPU cores (the highest efficiency)

Processing complex algorithms (mainly CPU operations, high pressure), the number of threads = the number of CPU cores (the highest efficiency)

Create thread

Thread function

Each thread has a unique thread ID of pthread type_ t. This ID is an unsigned long integer. If you want to get the thread ID of the current thread, you can call the following function:

pthread_t pthread_self(void); //Returns the current thread ID

In a process, calling a thread to create a function can get a child thread, which is different from the process. It needs to specify a processing function for every thread created, otherwise the thread will not work.

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
// Compile and link with -pthread. The name of the thread library is pthread. The full name is libpthread so libptread. a
  • Parameters:
    Thread: outgoing parameter, which is an unsigned long integer. If the thread is created successfully, the thread ID will be written to the memory pointed to by this pointer
    attr: the attribute of the thread. Generally, the default attribute can be used. Write NULL
    start_routine: function pointer, the processing action of the created sub thread, that is, the function is executed in the sub thread.
    arg: passed as an argument to start_ The routine pointer points to the inside of the function

  • Return value: 0 is returned for successful thread creation, and the corresponding error number is returned for failed thread creation

Create thread

// pthread_create.c 
#include <stdio.h>
#include <pthread.h>

// Processing code of child thread
void* working(void* arg)
{
    printf("I'm a child thread, thread  ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
        printf("child == i: = %d\n", i);
    }
    return NULL;
}

int main()
{
    // 1. Create a child thread
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("The child thread was created successfully, thread  ID: %ld\n", tid);
    // 2. The sub thread will not execute the following code, but the main thread will execute it
    printf("I'm the main thread, thread  ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
        printf("i = %d\n", i);
    }
    
    // Take a break, take a break Otherwise, the child thread may not be able to grab the CPU
    sleep(1);
    
    return 0;
}

Thread exit

When writing a multithreaded program, if you want the thread to exit without releasing the virtual address space (for the main thread), you can call the thread exit function in the thread library. As long as you call this function, the current thread will exit immediately and will not affect the normal operation of other threads, It can be used either in the child thread or in the main thread.

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

Parameter: the data carried when the thread exits. The main thread of the current sub thread will get the data. Specify NULL if not required

Thread recycling

Thread recycling function

Like a process, when a child thread exits, its kernel resources are mainly recycled by the main thread. The thread recycling function provided in the thread library is called pthread_join() is a blocking function. If there are still sub threads running, calling this function will block. The sub thread exits the function to unblock and recycle resources. When the function is called once, only one sub thread can be recycled. If there are multiple sub threads, recycling is required.

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
  • Parameters:
    Thread: the thread ID of the child thread to be recycled
    retval: the secondary pointer refers to the address of the primary pointer. It is an outgoing parameter. Pthread is stored in this address_ The data passed by exit() can be specified as NULL if this parameter is not required
  • Return value: 0 is returned for successful thread recycling, and the error number is returned for failed recycling.

Reclaim child thread data

Pthread can be used when the child thread exits_ The parameter of exit () transfers the data out. When recycling this sub thread, you can use pthread_ The second parameter of join () to receive the data passed by the child thread.

#include <stdio.h>
#include <pthread.h>

struct Test
{
    int num;
    int age;
};
struct Test t;

void* callback(void *arg)
{
    for(int i = 0; i < 5; ++i)
    {
        printf("zi: i = %d\n", i);
    }
    printf("zi : %ld \n", pthread_self());
    t.num = 100;
    t.age = 10;
    pthread_exit(&t);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, callback, NULL);
    
    printf("zhu: %ld\n", pthread_self());
    
    void *ptr;
    pthread_join(tid, &ptr);
    struct Test *t = (struct Test*)ptr;
    printf("num = %d, age = %d\n", t->num, t->age);
    return 0;
}

Thread separation

In some cases, the main thread in the program has its own business processing process. If the main thread is responsible for resource recovery of sub threads, pthread is called_ Join () as long as the child thread does not exit, the main thread will always be blocked, and the tasks of the main thread cannot be executed.

The thread separation function pthread is provided in the online library function_ Detach(), after calling this function, the specified sub thread can be separated from the main thread. When the sub thread exits, the kernel resources occupied by it will be taken over and recycled by other processes of the system. After thread separation, pthread is used in the main thread_ Join () can't reclaim child thread resources.

#include <pthread.h>
// The parameter is the thread ID of the child thread, and the main thread can be separated from the child thread
int pthread_detach(pthread_t thread);
#include <stdio.h>
#include <pthread.h>

void* callback(void* arg)
{
	printf("I'm a child thread, thread  ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
        printf("child == i: = %d\n", i);
    }
    return NULL;
}

int main()
{
	pthread_t pid;
	pthread_create(&pid, NULL, callback, NULL);
    printf("The child thread was created successfully, thread  ID: %ld\n", tid);
    // The sub thread will not execute the code below, and the main thread will execute
    printf("I'm the main thread, thread  ID: %ld\n", pthread_self());

	// Set the separation of child thread and main thread
	pthread_detach(tid);
	
	// Main thread exit
	pthread_exit(NULL);
	
	return 0;
}

Other thread functions

Thread cancellation

Thread cancellation means killing another thread in one thread under certain circumstances. Using this function to kill a thread requires two steps:

  1. Calling thread in thread A to cancel function pthread_cancel specifies to kill thread B. at this time, thread B cannot die
  2. In thread B, the process makes a system call (switching from the user area to the kernel area), otherwise thread B can run all the time.
#include <pthread.h>
// Parameter is the ID of the child thread
int pthread_cancel(pthread_t thread);

Thread ID comparison

In Linux, the thread ID is essentially an unsigned long integer, so you can directly use the comparison operator to compare the IDs of two threads, but the thread library can be used across platforms, pthread on some platforms_ T may not be a simple shaping. In this case, the comparison function must be used to compare the IDs of two threads. The function prototype is as follows:

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
  • Parameters: t1 and t2 are the thread ID s of the threads to be compared
  • Return value: if two thread ID s are equal, return a non-zero value; if not, return 0

summary

#include <pthread.h>
//Create child thread
pthread_t tid;
pthread_create(&tid, NULL, callback, NULL);
//The fourth parameter can also be an argument passed to a child thread
pthread_create(&tid, NULL, callback, void* arg);

//Thread exit
pthread_exit(NULL); 
//Or the address of the data returned by the thread
pthread_exit(void* retval);

//Thread recycling
pthread_join(tid, NULL); 
//Or a secondary pointer to the child thread pthread_ Pointer to the return parameter of exit (void * retval)
pthread_join(tid, void **retval);

reference resources

Thread_ Big C who loves programming

Keywords: Linux Multithreading Operating System kernel

Added by php-n00b on Sun, 23 Jan 2022 00:58:55 +0200