Linux Thread Programming
I. Core Theoretical Foundations of Threads
1. What is a Thread?
- Definition: A thread is an execution unit within a process, also referred to as a “Lightweight Process (LWP)”. It belongs to a specific process and shares the process’s resources (code segment, data segment, file descriptors, etc.).
- Core Purpose: Enable concurrent execution by splitting time-consuming tasks into multiple threads for parallel processing, thereby improving program efficiency (e.g., video rendering, concurrent network requests).
2. Core Characteristics of Threads
| Characteristic | Explanation |
|---|---|
| Resource Allocation | Processes are the system’s smallest unit of resource allocation; threads do not have independent resources (share process resources). |
| Execution Unit | Threads are the system’s smallest unit of execution and the basic object of CPU scheduling. |
| Hierarchical Relationship | Threads within a process are peer-to-peer; a “main thread” (the thread where themainfunction runs) exists by default. |
| Resource Sharing | Threads share the process’s global variables, static variables, file descriptors, etc.; only the stack area (8MB) is independent. |
| Stability | Threads are unstable: a single thread crash will cause the entire process to exit; processes are relatively stable and isolated from each other. |
| Creation Overhead | Thread creation only requires allocating an independent stack area (8MB), while process creation requires allocating a 3GB virtual address space (much higher overhead). |
| Concurrency Efficiency | Threads have higher concurrency than processes; switching between threads within the same process does not require address space switching, resulting in higher efficiency. |
3. Core Differences Between Threads and Processes
| Comparison Dimension | Thread | Process |
|---|---|---|
| Resource Allocation | Shares resources of the parent process; no independent address space. | Has an independent address space and independent resources (code segment, data segment, etc.). |
| Creation/Switching Overhead | Low (only stack area allocation). | High (full address space allocation). |
| Communication Method | Directly access shared variables; simple communication. | Requires IPC (pipes, message queues, etc.); complex communication. |
| Stability | Low (thread crash leads to process exit). | High (processes are isolated from each other). |
| Concurrency Efficiency | High (thread switching without address space switching). | Low (high process switching overhead). |
4. Core Workflow of Thread Programming (POSIX Standard)
- Create Multiple Threads: Use
pthread_createto create child threads and specify the thread execution function. - Thread Task Execution: Child threads complete specific tasks (resource operations, computations, etc.) in the callback function.
- Thread Resource Recycling: Release thread resources using
pthread_join(blocking recycling) orpthread_detach(automatic recycling) to avoid memory leaks.
5. Detailed Explanation of Key Thread Functions
The POSIX thread library (libpthread) provides core interfaces for thread operations. Below are explanations of commonly used functions:
| Function Prototype | Function Description |
|---|---|
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) | Creates a child thread. - thread: Output parameter to store the new thread ID.- attr: Thread attributes (useNULLfor default).- start_routine: Thread callback function (execution entry).- arg: Parameter for the callback function.- Return value: 0on success; error code on failure. |
pthread_t pthread_self(void) | Gets the current thread ID. - Return value: ID of the current thread (type unsigned long, use%lufor printing). |
void pthread_exit(void *retval) | Child thread exits actively. - retval: Exit status of the thread (returned to the main thread). |
int pthread_cancel(pthread_t thread) | Main thread cancels a specified child thread. - thread: Target thread ID.- Return value: 0on success; error code on failure. |
int pthread_join(pthread_t thread, void **retval) | Blocks to recycle child thread resources. - thread: ID of the thread to recycle.- retval: Receives the exit status of the child thread.- Return value: 0on success; error code on failure. |
int pthread_detach(pthread_t thread) | Sets the thread detach attribute (resources are automatically recycled after exit). - No need for the main thread to call pthread_join. |
6. Thread Viewing Commands
# View all threads in the system (PID: Process ID, LWP: Thread ID, COMM: Thread name)ps-eLo pid,ppid,lwp,stat,comm# View detailed thread information (including CPU usage, memory, etc.)ps-eLfII. Practical Code Analysis (8 Core Examples)
The following 8 practical code examples, from basic to advanced, will help you gradually master thread programming skills (all code must be compiled with thepthreadlibrary:gcc filename.c -o filename -lpthread).
Example 01: Create Multiple Threads (01pthread.c)
Function: Create 2 child threads to execute different tasks (sending videos, receiving controls)
#include<stdio.h>#include<pthread.h>#include<stdlib.h>#include<unistd.h>#include<string.h>// Thread 1 callback function: Send videovoid*thread_function(void*arg){while(1){printf("Sending video...\n");sleep(1);// Execute every 1 second}returnNULL;}// Thread 2 callback function: Receive controlvoid*thread_function2(void*arg){while(1){printf("Receiving control...\n");sleep(1);}returnNULL;}intmain(){pthread_tthread_id;// Thread 1 IDpthread_tthread_id2;// Thread 2 ID// Create thread 1pthread_create(&thread_id,NULL,thread_function,NULL);// Create thread 2pthread_create(&thread_id2,NULL,thread_function2,NULL);// Main thread blocks (prevents main thread exit from terminating child threads)while(1){sleep(1);}return0;}Key Notes:
pthread_createparameters: Thread ID pointer, default attributes (NULL), callback function, callback function parameter (NULL).- The main thread must remain running (
while(1)); otherwise, the entire process terminates after the main thread exits, and child threads are destroyed. - Compilation command:
gcc 01pthread.c -o 01pthread -lpthread. - Running result: The two child threads alternately output “Sending video…” and “Receiving control…”, achieving concurrent execution.
Example 02: Get Thread ID (02pthread_self.c)
Function: Get the IDs of the main thread and child threads usingpthread_self()
#include<stdio.h>#include<pthread.h>#include<unistd.h>#include<stdlib.h>#include<string.h>void*th1(void*arg){while(1){// Print child thread 1 ID (%lu corresponds to unsigned long type)printf("Sending video...tid:%lu\n",pthread_self());sleep(1);}returnNULL;}void*th2(void*arg){while(1){printf("Receiving control...tid:%lu\n",pthread_self());sleep(1);}returnNULL;}intmain(){pthread_ttid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);// Print main thread IDwhile(1){printf("main tid:%lu\n",pthread_self());sleep(1);}return0;}Key Notes:
pthread_self()has no parameters and returns the ID of the current thread (typepthread_t, recommended to use%lufor formatted output).- Running result: The main thread and two child threads output their respective IDs. You can verify the existence of threads using
ps -eLo lwp,comm.
Example 03: Thread Exit (03pthread_exit.c)
Function: The child thread exits actively usingpthread_exit(), and the main thread exits after running a specified number of times
#include<stdio.h>#include<pthread.h>#include<stdlib.h>#include<unistd.h>#include<string.h>void*th(void*arg){while(1){printf("sub_th %lu\n",pthread_self());sleep(1);}pthread_exit(NULL);// Child thread exits actively (unreachable here due to while(1), for demonstration only)}intmain(){pthread_ttid;pthread_create(&tid,NULL,th,NULL);inti=8;// Main thread exits after running for 8 secondswhile(i--){printf("main_th %lu\n",pthread_self());sleep(1);}return0;}Key Notes:
pthread_exit(NULL): The child thread exits actively, with the parameter being the exit status (NULLmeans no return value).- Note: After the main thread exits, child threads are forcibly terminated (even if the child thread has
while(1)). - Running result: The main thread outputs 8 times and exits, and the child thread terminates simultaneously.
Example 04: Cancel Thread (04phread_cancel.c)
Function: The main thread cancels a child thread usingpthread_cancel()
#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<unistd.h>#include<string.h>void*thread_func(void*arg){while(1){printf("subth %lu\n",pthread_self());sleep(1);}}intmain(){pthread_ttid;pthread_create(&tid,NULL,thread_func,NULL);inti=0;while(1){printf("main th %lu\n",pthread_self());sleep(1);i++;if(i==2){// Cancel the child thread after running for 2 secondspthread_cancel(tid);printf("Child thread canceled\n");}}return0;}Key Notes:
pthread_cancel(tid): Sends a cancellation request to the specified child thread, which responds at a “cancellation point” (e.g., system calls likesleeporprintf).- Running result: The child thread outputs twice and is canceled, while the main thread continues running.
- Note:
pthread_cancelonly sends a request. If the child thread has no cancellation points (e.g., a pure computation loop), you need to manually callpthread_testcancel()to set a cancellation point.
Example 05: Thread Resource Recycling (05pthread_jion.c)
Function: The main thread blocks to wait for the child thread to complete and recycles resources usingpthread_join()
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<pthread.h>void*th(void*arg){inti=5;while(i--){printf("workth,%lu\n",pthread_self());sleep(1);}returnNULL;}intmain(intargc,char**argv){pthread_ttid;pthread_create(&tid,NULL,th,NULL);// Block to wait for the child thread tid to complete and recycle its resourcespthread_join(tid,NULL);printf("Child thread finished, main thread exiting\n");return0;}Key Notes:
pthread_join(tid, NULL): The main thread blocks until the child threadtidexits, preventing the child thread from becoming a “zombie thread” (unrecycled resources).- The second parameter is
NULL, indicating no interest in the child thread’s exit status. - Running result: The child thread outputs 5 times and exits, and the main thread prints a message and exits.
Example 06: Get Thread Return Value (06pthread_jionret.c)
Function: The child thread dynamically allocates memory and returns data; the main thread gets the return value and frees the memory usingpthread_join()
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<pthread.h>#include<unistd.h>void*th(void*arg){// Dynamically allocate memory (stack data of the child thread cannot be returned, as it is released when the thread exits)char*str=(char*)malloc(20);strcpy(str,"I'm exiting");returnstr;// Return the dynamically allocated memory address}intmain(intargc,char*argv[]){pthread_ttid;pthread_create(&tid,NULL,th,NULL);void*ret=NULL;// Recycle the child thread and get the return value (ret points to the memory allocated by the child thread)pthread_join(tid,&ret);printf("Child thread return value: %s\n",(char*)ret);free(ret);// Free the memory allocated by the child thread to avoid memory leaksreturn0;}Key Notes:
- The child thread’s return value cannot be a stack variable (the stack is released when the thread exits); use
mallocfor dynamic memory allocation. - The main thread receives the return value through the second parameter
&retofpthread_joinand must manuallyfreeit after use. - Running result: The main thread prints the string “I’m exiting” returned by the child thread.
Example 07: Pass Struct Parameters (07.c)
Function: The main thread passes struct parameters to the child thread; the child thread prints and returns the struct address
#include<stdio.h>#include<pthread.h>#include<stdlib.h>#include<unistd.h>#include<string.h>// Define a struct (store user information)typedefstruct{charname[20];intage;charaddress[50];}PER;void*th(void*arg){// Convert void* type to struct pointerPER*p=(PER*)arg;printf("Information received by child thread:\n");printf("name:%s\n",p->name);printf("age:%d\n",p->age);printf("address:%s\n",p->address);returnp;// Return the struct address}intmain(intargc,char*argv[]){PER p={0};printf("Input name: ");fgets(p.name,sizeof(p.name),stdin);p.name[strlen(p.name)-1]='\0';// Remove the newline charactercharbuf[20]={0};printf("Input age: ");fgets(buf,sizeof(buf),stdin);p.age=atoi(buf);// Convert string to integer and assign to p.ageprintf("Input address: ");fgets(p.address,sizeof(p.address),stdin);p.address[strlen(p.address)-1]='\0';// Remove the newline characterpthread_ttid;// Pass the struct address to the child threadpthread_create(&tid,NULL,th,&p);void*ret=NULL;pthread_join(tid,&ret);printf("Returned struct address: %p\n",ret);// Verify the returned dataprintf("Verified age: %d\n",((PER*)ret)->age);return0;}Key Notes:
- To pass multiple parameters to a thread, encapsulate them in a struct and pass the struct address (avoids type conversion issues with multiple parameters).
- The struct is allocated in the main thread’s stack; ensure the main thread does not exit before the child thread finishes using the struct (otherwise, the stack is released, leading to wild pointers).
- Running result: The child thread prints the user information passed by the main thread, and the main thread verifies the returned struct data.
Example 08: Thread Resource Sharing (08.c)
Function: Demonstrate resource sharing between threads (modify global variables)
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<pthread.h>inta=20;// Global variable (shared by all threads)void*thread(void*arg){a+=10;// Child thread modifies the global variableprintf("Child thread: a = %d\n",a);returnNULL;}intmain(){pthread_ttid;pthread_create(&tid,NULL,thread,NULL);// Wait for the child thread to complete to ensure the modification takes effectpthread_join(tid,NULL);printf("Main thread: a = %d\n",a);// Main thread reads the modified global variablereturn0;}Key Notes:
- Threads share global variables and static variables; modifications to global variables by child threads are visible to the main thread.
- If multiple threads modify shared resources simultaneously, synchronization mechanisms (e.g., mutexes
pthread_mutex_t) are required to avoid race conditions. - Running result: The child thread outputs
a = 30, and the main thread also outputsa = 30, confirming resource sharing.
III. Summary of Key Points
- Thread Creation: Use
pthread_createto create threads, specifying the callback function and parameters; remember to link thepthreadlibrary during compilation (-lpthread). - Thread ID: Use
pthread_self()to get the current thread ID, formatted with%lu. - Thread Exit: Child threads can exit actively with
pthread_exit()(returning status) or be canceled by the main thread withpthread_cancel(). - Resource Recycling: Use
pthread_join()for blocking recycling (to get the return value) orpthread_detach()for automatic recycling (to avoid memory leaks). - Resource Sharing: Threads share global variables and static variables, but independent stack areas require dynamic memory allocation for cross-thread data transfer.
- Common Pitfalls:
- Forgetting to link the
pthreadlibrary during compilation (leading to undefined reference errors). - Returning stack variables from child threads (stack release causes wild pointers).
- Not recycling thread resources (leading to zombie threads and memory leaks).
- Race conditions when multiple threads modify shared resources (needing synchronization mechanisms).
- Forgetting to link the
By mastering the theoretical knowledge and practical skills in this article, you can efficiently implement concurrent programming in Linux using POSIX threads. For complex scenarios (e.g., thread synchronization, deadlock prevention), further study of mutexes, condition variables, and other advanced technologies is recommended.