Quick start to Linux Multithreaded Programming

  • 2020-06-23 02:34:06
  • OfStack

This article mainly to Linux under the multithreading 1 introduction, although it is an introduction, but 10 minutes in detail, I hope you through this article, Linux multithreaded programming concept has a definite understanding. The details are as follows.

1 thread basic knowledge

The process is the basic unit of resource management, while the thread is the basic unit of system scheduling. The thread is the smallest unit that the operating system can perform scheduling operations. It is included in the process and is the actual operating unit of the process. A thread refers to a single 1-order flow of control in a process, in which multiple threads can be concurrent, each executing a different task in parallel.

A process can only do one thing at a time. With multiple threads of control, the program is designed to do more than one thing at a time, with each thread handling individual tasks.

It's important to note that you can get the benefits of the multithreaded programming model even if your program runs on a single core processor. The number of processors does not affect the structure of the program, so the program can be simplified by threads regardless of the number of processors.

The linux operating system USES the POSIX compliant thread as the system standard thread, which defines API for a whole set of operating threads.

2. Thread identification

Unlike a process that has one ID1 and one thread per thread, ID has only one thread in the system, while a thread is attached to a process and its thread, ID, is meaningful only in the process to which it belongs. The thread ID is represented by pthread_t.


//pthread_self Returns directly from the calling thread ID
include <pthread.h>
pthread_t pthread_self(void);

It makes no sense to determine the size of two threads, ID, but sometimes it may be necessary to determine whether two given threads, ID, are equal, using the following interface:


//pthread_equal if t1 and t2 The specified thread ID Same thing, return 0 ; Otherwise return non 0 Value. 
include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);

3. Thread creation

The lifetime of a thread begins at the moment it is created. The interface of the thread is created:


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

Function parameters:


thread( Output parameters ) By the pthread_create The thread handle returned after a successful thread creation that operates on subsequent threads API Is used to mark the newly created thread;  
start_routine( The input parameters ) , the entry function of the new thread;  
arg( The input parameters ) , the parameters passed to the new thread entry function;  
attr( The input parameters ) , specify the attributes of the newly created thread, such as thread stack size, etc.; If the value is NULL , indicates the use of system default properties. 

Function return value:


 Successful, return 0 ;  
 Failure, return related error code. 

Note:

1. Main thread, which is the initial thread of a process, its entry function is main function.
2. When a new thread is created, a thread may not be executed immediately after it is created, or even not executed after the end of the thread that created it; It is also possible that the new thread is already running before the current thread from pthread_create, or even before the new thread is returned from the current thread from pthread_create.

Program example:


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

void printids(const char *s){
 pid_t pid;
 pthread_t tid;
 pid = getpid();
 tid = pthread_self();
 printf("%s, pid %lu tid %lu (0x%lx)\n",s,(unsigned long)pid,(unsigned long)tid,
 (unsigned long)tid);
}

void *thread_func(void *arg){
 printids("new thread: ");
 return ((void*)0);
}
int main() {
 int err;
 pthread_t tid;
 err = pthread_create(&tid,NULL,thread_func,NULL);
 if (err != 0) {
  fprintf(stderr,"create thread fail.\n");
 exit(-1); 
 }
 printids("main thread:");
 sleep(1); 
 return 0;
}

Note that in the above program, the main thread sleeps for 1 second. If it does not sleep, then the main thread does not sleep, then it may exit, so that the new thread may not be run. I commented out the sleep function myself, and found that many times to let the new thread output.

Compile command:


gcc -o thread thread.c -lpthread

The operation results are as follows:


main thread:, pid 889 tid 139846854309696 (0x7f30a212f740)
new thread: , pid 889 tid 139846845961984 (0x7f30a1939700)

You can see that the process ID for both threads is the same. It shares resources in the process.

4. Thread termination

Thread termination comes in two forms: passive termination and active termination

There are two ways of passive termination:

1. Process termination. Any thread executing exit, _Exit, or _exit will cause process termination, which will cause the termination of all threads attached to the process.
2. Another thread calls pthread_cancel to cancel the thread.

There are also two ways to terminate voluntarily:

1. Execute the return statement in the entry function of the thread. Execution of the return statement in the main function (the main thread entry function) causes the process to terminate, resulting in the termination of all threads attached to the process.
2. The thread calls the pthread_exit function and the main function (the main thread entry function) calls the pthread_exit function. The main thread terminates.

Thread termination function:


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

A thread calling the pthread_exit function causes the calling thread to terminate and returns the content specified by retval.
Note :retval cannot point to the stack space of this thread, or it could become a wild pointer!

5. Manage thread termination

5.1 Connection of threads

The termination of one thread is an asynchronous event for another thread. Sometimes we want to wait for an ID thread to terminate before performing some operation. The pthread_join function provides this function, which is called thread connection:


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

Parameter description:


//pthread_equal if t1 and t2 The specified thread ID Same thing, return 0 ; Otherwise return non 0 Value. 
include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
0

The return value:


//pthread_equal if t1 and t2 The specified thread ID Same thing, return 0 ; Otherwise return non 0 Value. 
include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
1

When thread X connects to thread Y, if thread Y is still running, thread X will block until thread Y terminates. If thread Y terminates before it is connected, the connection call to thread X returns immediately.

The connection thread actually has another layer of meaning. After a thread terminates, if no one connects it, the system will not be able to recover the resources occupied by the terminated thread, and the terminated thread will also become a zombie thread. Therefore, when we connect to a thread, we are telling the system that the resource of the terminated thread can be reclaimed.

Note: reconnecting to a thread that has already been connected will cause unpredictable behavior!

5.2 Thread separation

Sometimes we don't care if a thread has terminated, we just want the system to automatically recycle the resources used by the terminated thread. The pthread_detach function provides us with this capability, which is called thread separation:


//pthread_equal if t1 and t2 The specified thread ID Same thing, return 0 ; Otherwise return non 0 Value. 
include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
2

By default, a thread terminates and the system is required to recover its resources after it has been connected. If we call the pthread_detach function to separate a thread, the system will automatically recycle its resources when the thread terminates.


/*
*  The file name:  thread_sample1.c
*  Description: Demonstrates basic operations of threads 
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

/* The child thread 1 The entry function */
void *thread_routine1(void *arg)
{
 fprintf(stdout, "thread1: hello world!\n");
 sleep(1);
 /* The child thread 1 In the exit */
 return NULL;
}

/* The child thread 2 The entry function */
void *thread_routine2(void *arg)
{

 fprintf(stdout, "thread2: I'm running...\n");
 pthread_t main_thread = (pthread_t)arg;

 /* Separate the self, it can no longer be connected */
 pthread_detach(pthread_self());

 /* Judge main thread ID And the child thread 2ID Are equal */
 if (!pthread_equal(main_thread, pthread_self())) {
  fprintf(stdout, "thread2: main thread id is not equal thread2\n");
 }

 /* Wait for the main thread to terminate */
 pthread_join(main_thread, NULL);
 fprintf(stdout, "thread2: main thread exit!\n");

 fprintf(stdout, "thread2: exit!\n");
 fprintf(stdout, "thread2: process exit!\n");
 /* The child thread 2 Terminates here and the process exits */
 pthread_exit(NULL);
}

int main(int argc, char *argv[])
{

 /* Create child threads 1*/
 pthread_t t1;
 if (pthread_create(&t1, NULL, thread_routine1, NULL)!=0) {
  fprintf(stderr, "create thread fail.\n");
  exit(-1);
 }
 /* Waiting child thread 1 Termination of */
 pthread_join(t1, NULL);
 fprintf(stdout, "main thread: thread1 terminated!\n\n");

 /* Create child threads 2 , and will be the main thread ID To the child thread 2*/
 pthread_t t2;
 if (pthread_create(&t2, NULL, thread_routine2, (void *)pthread_self())!=0) {
  fprintf(stderr, "create thread fail.\n");
  exit(-1);
 }

 fprintf(stdout, "main thread: sleeping...\n");
 sleep(3);
 /* Main thread usage pthread_exit The function terminates and the process continues */
 fprintf(stdout, "main thread: exit!\n");
 pthread_exit(NULL); 

 fprintf(stdout, "main thread: never reach here!\n");
 return 0;
}

The final execution results are as follows:


//pthread_equal if t1 and t2 The specified thread ID Same thing, return 0 ; Otherwise return non 0 Value. 
include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
4

conclusion

That's all for this quick start to Linux multithreaded programming. Interested friends can continue to refer to other related topics in this site, if there is any deficiency, welcome to comment out. Thank you for your support!


Related articles: