DAY32 Linux Thread Programming

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 the main function 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)

  1. Create Multiple Threads : Use pthread_create to create child threads and specify the thread execution function.
  2. Thread Task Execution: Child threads complete specific tasks (resource operations, computations, etc.) in the callback function.
  3. Thread Resource Recycling : Release thread resources using pthread_join (blocking recycling) or pthread_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 (use NULL for default). - start_routine: Thread callback function (execution entry). - arg: Parameter for the callback function. - Return value: 0 on 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 %lu for 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: 0 on 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: 0 on 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

bash 复制代码
# 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 -eLf

II. 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 the pthread library: 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)
c 复制代码
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

// Thread 1 callback function: Send video
void *thread_function(void *arg)
{
    while (1) {
        printf("Sending video...\n");
        sleep(1); // Execute every 1 second
    }
    return NULL;
}

// Thread 2 callback function: Receive control
void *thread_function2(void *arg)
{
    while (1) {
        printf("Receiving control...\n");
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t thread_id;  // Thread 1 ID
    pthread_t thread_id2; // Thread 2 ID

    // Create thread 1
    pthread_create(&thread_id, NULL, thread_function, NULL);
    // Create thread 2
    pthread_create(&thread_id2, NULL, thread_function2, NULL);

    // Main thread blocks (prevents main thread exit from terminating child threads)
    while (1) {
        sleep(1);
    }
    return 0;
}
Key Notes:
  1. pthread_create parameters: Thread ID pointer, default attributes (NULL), callback function, callback function parameter (NULL).
  2. The main thread must remain running (while(1)); otherwise, the entire process terminates after the main thread exits, and child threads are destroyed.
  3. Compilation command: gcc 01pthread.c -o 01pthread -lpthread.
  4. 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 using pthread_self()
c 复制代码
#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);
    }
    return NULL;
}

void *th2(void *arg)
{
    while (1) {
        printf("Receiving control...tid:%lu\n", pthread_self());
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, th1, NULL);
    pthread_create(&tid2, NULL, th2, NULL);

    // Print main thread ID
    while (1) {
        printf("main tid:%lu\n", pthread_self());
        sleep(1);
    }
    return 0;
}
Key Notes:
  1. pthread_self() has no parameters and returns the ID of the current thread (type pthread_t, recommended to use %lu for formatted output).
  2. 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 using pthread_exit(), and the main thread exits after running a specified number of times
c 复制代码
#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)
}

int main(){
    pthread_t tid;
    pthread_create(&tid, NULL, th, NULL);

    int i = 8;
    // Main thread exits after running for 8 seconds
    while (i--) {
        printf("main_th %lu\n", pthread_self());
        sleep(1);
    }
    return 0;
}
Key Notes:
  1. pthread_exit(NULL): The child thread exits actively, with the parameter being the exit status (NULL means no return value).
  2. Note: After the main thread exits, child threads are forcibly terminated (even if the child thread has while(1)).
  3. 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 using pthread_cancel()
c 复制代码
#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);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);

    int i = 0;
    while (1) {
        printf("main th %lu\n", pthread_self());
        sleep(1);
        i++;
        if (i == 2) {
            // Cancel the child thread after running for 2 seconds
            pthread_cancel(tid);
            printf("Child thread canceled\n");
        }
    }
    return 0;
}
Key Notes:
  1. pthread_cancel(tid): Sends a cancellation request to the specified child thread, which responds at a "cancellation point" (e.g., system calls like sleep or printf).
  2. Running result: The child thread outputs twice and is canceled, while the main thread continues running.
  3. Note: pthread_cancel only sends a request. If the child thread has no cancellation points (e.g., a pure computation loop), you need to manually call pthread_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 using pthread_join()
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

void* th(void* arg)
{
    int i = 5;
    while (i--) {
        printf("workth,%lu\n", pthread_self());
        sleep(1);
    }
    return NULL;
}

int main(int argc, char **argv)
{
    pthread_t tid;
    pthread_create(&tid, NULL, th, NULL);

    // Block to wait for the child thread tid to complete and recycle its resources
    pthread_join(tid, NULL);
    printf("Child thread finished, main thread exiting\n");
    return 0;
}
Key Notes:
  1. pthread_join(tid, NULL): The main thread blocks until the child thread tid exits, preventing the child thread from becoming a "zombie thread" (unrecycled resources).
  2. The second parameter is NULL, indicating no interest in the child thread's exit status.
  3. 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 using pthread_join()
c 复制代码
#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");
    return str; // Return the dynamically allocated memory address
}

int main(int argc, char* argv[])
{
    pthread_t tid;
    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 leaks
    return 0;
}
Key Notes:
  1. The child thread's return value cannot be a stack variable (the stack is released when the thread exits); use malloc for dynamic memory allocation.
  2. The main thread receives the return value through the second parameter &ret of pthread_join and must manually free it after use.
  3. 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
c 复制代码
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

// Define a struct (store user information)
typedef struct
{
    char name[20];
    int age;
    char address[50];
} PER;

void* th(void* arg)
{
    // Convert void* type to struct pointer
    PER* 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);
    
    return p; // Return the struct address
}

int main(int argc, 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 character

    char buf[20] = {0};
    printf("Input age: ");
    fgets(buf, sizeof(buf), stdin);
    p.age = atoi(buf); // Convert string to integer and assign to p.age

    printf("Input address: ");
    fgets(p.address, sizeof(p.address), stdin);
    p.address[strlen(p.address) - 1] = '\0'; // Remove the newline character

    pthread_t tid;
    // Pass the struct address to the child thread
    pthread_create(&tid, NULL, th, &p);

    void* ret = NULL;
    pthread_join(tid, &ret);
    printf("Returned struct address: %p\n", ret);
    // Verify the returned data
    printf("Verified age: %d\n", ((PER*)ret)->age);
    return 0;
}
Key Notes:
  1. To pass multiple parameters to a thread, encapsulate them in a struct and pass the struct address (avoids type conversion issues with multiple parameters).
  2. 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).
  3. 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)
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

int a = 20; // Global variable (shared by all threads)

void *thread(void *arg)
{
    a += 10; // Child thread modifies the global variable
    printf("Child thread: a = %d\n", a);
    return NULL;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread, NULL);

    // Wait for the child thread to complete to ensure the modification takes effect
    pthread_join(tid, NULL);
    printf("Main thread: a = %d\n", a); // Main thread reads the modified global variable
    return 0;
}
Key Notes:
  1. Threads share global variables and static variables; modifications to global variables by child threads are visible to the main thread.
  2. If multiple threads modify shared resources simultaneously, synchronization mechanisms (e.g., mutexes pthread_mutex_t) are required to avoid race conditions.
  3. Running result: The child thread outputs a = 30, and the main thread also outputs a = 30, confirming resource sharing.

III. Summary of Key Points

  1. Thread Creation : Use pthread_create to create threads, specifying the callback function and parameters; remember to link the pthread library during compilation (-lpthread).
  2. Thread ID : Use pthread_self() to get the current thread ID, formatted with %lu.
  3. Thread Exit : Child threads can exit actively with pthread_exit() (returning status) or be canceled by the main thread with pthread_cancel().
  4. Resource Recycling : Use pthread_join() for blocking recycling (to get the return value) or pthread_detach() for automatic recycling (to avoid memory leaks).
  5. Resource Sharing: Threads share global variables and static variables, but independent stack areas require dynamic memory allocation for cross-thread data transfer.
  6. Common Pitfalls :
    • Forgetting to link the pthread library 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).

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.

相关推荐
C语言魔术师2 小时前
【linux】linux进程概念(四)(环境变量)
linux·运维·服务器
eggrall2 小时前
《gdb 与 cgdb 深度解析:命令行调试的效率革命》
linux
LYFlied2 小时前
【每日算法】LeetCode 234. 回文链表详解
算法·leetcode·链表
秦jh_2 小时前
【Qt】常用控件(上)
服务器·数据库·qt
晨曦夜月2 小时前
头文件与目标文件的关系
linux·开发语言·c++
Xyz996_2 小时前
Ansible进行Nginx编译安装的详细步骤
运维·ansible
白仑色2 小时前
java中的anyMatch和allMatch方法
java·linux·windows·anymatch·allmatch
NeDon2 小时前
[OJ]数据结构:移除链表元素
c语言·数据结构·算法·链表
刃神太酷啦2 小时前
C++ list 容器全解析:从构造到模拟实现的深度探索----《Hello C++ Wrold!》(16)--(C/C++)
java·c语言·c++·qt·算法·leetcode·list