Linux 应用编程:线程(Thread)详解

在上一章中,我们学习了进程相关的知识,对进程有了一个比较全面的认识和理解。本章将开始学习 Linux 应用编程中非常重要的编程技巧——线程(Thread)。与进程类似,线程是允许应用程序并发执行多个任务的一种机制。线程参与系统调度,事实上,系统调度的最小单元是线程,而并非进程。虽然线程的概念比较简单,但其所涉及的内容比较多,因此本章篇幅会相对较长。希望大家能够坚持学习,掌握线程编程的核心技巧。

1. 线程的基本概念

1.1 什么是线程?

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的内存空间和资源,但每个线程有自己的栈空间和程序计数器。

1.2 线程 VS 进程

为了更好地理解线程,我们可以将线程与进程进行对比。下表展示了线程与进程的主要区别:

特性进程线程
定义程序的一次执行,拥有独立的内存空间进程中的一个执行单元,共享进程的内存空间
资源占用较多,每个进程有独立的内存空间较少,线程共享进程的内存空间
创建与销毁较慢,涉及资源的分配与回收较快,只需分配栈空间和程序计数器
上下文切换较慢,涉及内存空间的切换较快,只需切换栈和程序计数器
通信较复杂,通常需要进程间通信(IPC)机制较简单,可以直接共享内存
独立性独立,一个进程崩溃不会影响其他进程依赖,一个线程崩溃可能导致整个进程崩溃

1.3 线程的优点

  • 轻量级:线程的创建和销毁比进程更快,占用的资源更少。
  • 共享内存:线程之间可以直接共享内存,通信更加高效。
  • 并发性:多线程可以充分利用多核 CPU 的性能,提高程序的并发性。

2. 线程标识

在 Linux 中,每个线程都有一个唯一的线程标识符(Thread ID,TID)。线程标识符的类型是 pthread_t,它是一个不透明的数据类型,通常是一个整数或结构体。

2.1 获取线程 ID

可以使用 pthread_self() 函数获取当前线程的 ID。

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

void* thread_function(void* arg) {
    pthread_t tid = pthread_self();
    printf("Thread ID: %lu\n", (unsigned long)tid);
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
    return 0;
}

2.2 比较线程 ID

可以使用 pthread_equal() 函数比较两个线程 ID 是否相等。

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

void* thread_function(void* arg) {
    pthread_t tid = pthread_self();
    if (pthread_equal(tid, *(pthread_t*)arg)) {
        printf("This is the main thread.\n");
    } else {
        printf("This is a new thread.\n");
    }
    return NULL;
}

int main() {
    pthread_t main_tid = pthread_self();
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, &main_tid);
    pthread_join(thread, NULL);
    return 0;
}

3. 线程创建与回收

3.1 创建线程

在 Linux 中,可以使用 pthread_create() 函数创建线程。

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

void* thread_function(void* arg) {
    printf("Hello from thread!\n");
    return NULL;
}

int main() {
    pthread_t thread;
    int ret = pthread_create(&thread, NULL, thread_function, NULL);
    if (ret != 0) {
        printf("Error: pthread_create() failed\n");
        return 1;
    }
    pthread_join(thread, NULL);
    return 0;
}

3.2 回收线程

线程创建后,可以使用 pthread_join() 函数等待线程结束并回收资源。

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

void* thread_function(void* arg) {
    printf("Thread is running...\n");
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
    printf("Thread has finished.\n");
    return 0;
}

4. 线程取消

4.1 取消线程

可以使用 pthread_cancel() 函数取消一个线程。

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

void* thread_function(void* arg) {
    while (1) {
        printf("Thread is running...\n");
        sleep(1);
    }
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    sleep(3);
    pthread_cancel(thread);
    pthread_join(thread, NULL);
    printf("Thread has been canceled.\n");
    return 0;
}

4.2 设置取消状态

线程可以通过 pthread_setcancelstate()pthread_setcanceltype() 函数设置取消状态和取消类型。

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

void* thread_function(void* arg) {
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    while (1) {
        printf("Thread is running...\n");
        sleep(1);
    }
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    sleep(3);
    pthread_cancel(thread);
    pthread_join(thread, NULL);
    printf("Thread has been canceled.\n");
    return 0;
}

5. 线程终止

5.1 正常终止

线程可以通过返回或调用 pthread_exit() 函数正常终止。

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

void* thread_function(void* arg) {
    printf("Thread is exiting...\n");
    pthread_exit(NULL);
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
    printf("Thread has exited.\n");
    return 0;
}

5.2 强制终止

不建议使用 pthread_cancel() 强制终止线程,因为这可能导致资源泄漏或数据不一致。

6. 线程分离

6.1 分离线程

可以使用 pthread_detach() 函数将线程设置为分离状态,分离状态的线程在终止时会自动回收资源。

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

void* thread_function(void* arg) {
    printf("Thread is running...\n");
    sleep(2);
    printf("Thread is exiting...\n");
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_detach(thread);
    printf("Main thread is running...\n");
    sleep(3);
    printf("Main thread is exiting...\n");
    return 0;
}

6.2 分离状态的影响

分离状态的线程不能被 pthread_join() 回收,因此在使用 pthread_detach() 时需要确保线程的资源能够被正确释放。

7. 总结

本章详细介绍了 Linux 应用编程中的线程(Thread)相关知识,包括线程的基本概念、线程标识、线程创建与回收、线程取消、线程终止以及线程分离。通过本章的学习,你应该能够掌握线程编程的基本技巧,并能够在实际项目中应用这些知识。

线程编程虽然复杂,但它是提高程序并发性和性能的重要手段。希望你能通过不断实践,深入理解线程的工作原理,并能够在实际项目中灵活运用。

加油!