一、线程的基本概念
1.1 什么是线程?
线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存、文件描述符等),但每个线程拥有独立的执行栈和程序计数器。
线程其实就是我们之前所讲的进程的延申,进程实际上是可以由多个执行流组成的,这些执行流其实就叫做线程。我们之前所讲的进程实际上就是只有单执行流的特殊情况,进程实际上可以看作资源分配的实体,线程就是资源分配的基本单位。

1.2 线程与进程的区别
| 特性 |
进程 |
线程 |
| 定义 |
程序的一次执行,拥有独立的内存空间 |
进程中的一个执行流,共享进程的内存空间 |
| 资源开销 |
较大,创建和销毁开销大 |
较小,创建和销毁开销小 |
| 通信方式 |
进程间通信(IPC)机制复杂 |
线程间通信简单,直接共享内存 |
| 上下文切换 |
开销大 |
开销小 |
| 独立性 |
独立,互不影响 |
依赖进程,线程间相互影响 |
1.3 线程的优点
- 响应性:多线程程序可以在一个线程等待 I/O 时,另一个线程继续执行,从而提高程序的响应性。
- 资源共享:线程共享进程的内存空间,因此线程间的数据共享和通信更加方便。
- 经济性:创建和销毁线程的开销比进程小,且线程切换的开销也比进程小。
- 多核利用:多线程程序可以充分利用多核 CPU 的并行计算能力。
1.4 线程的缺点
- 同步问题:多个线程共享同一进程的资源,容易引发竞态条件等问题。
- 调试困难:多线程程序的调试比单线程程序复杂,因为线程的执行顺序是不确定的。
- 资源竞争:多个线程竞争同一资源时,可能导致性能下降。
这些问题我们会在后面的章节进行解决,尤其是线程的同步与互斥问题是很重要的,我们后面会进行讲解。
二、Linux 中的线程模型
2.1 用户级线程与内核级线程
在 Linux 中,线程的实现可以分为用户级线程和内核级线程。
- 用户级线程:由用户空间的线程库(如 POSIX 线程库)管理,内核并不知道这些线程的存在。用户级线程的创建、调度、同步等操作都由线程库在用户空间完成。优点是线程切换开销小,缺点是无法利用多核 CPU 的并行能力。
- 内核级线程:由操作系统内核管理,内核知道每个线程的存在,并负责线程的调度。内核级线程的创建、调度、同步等操作都需要通过系统调用来完成。优点是可以利用多核 CPU 的并行能力,缺点是线程切换开销较大。
2.2 Linux 的线程实现
Linux 通过轻量级进程(Lightweight Process, LWP)来实现线程。每个线程在内核中都有一个对应的轻量级进程,这些轻量级进程共享同一地址空间和其他资源。Linux 的线程库(如 NPTL)提供了对 POSIX 线程标准的支持。我们可以理解为 Linux 的线程实现就是用户层的,因为 Linux 中并没有线程的概念,有的只是轻量级进程的概念,是通过在用户层进行封装来实现的。
三、线程控制
3.1 线程的创建与终止
在 Linux 中,线程的创建和终止是通过 POSIX 线程库(pthread)来实现的。下面我们通过代码示例来讲解线程的创建与终止。
3.1.1 创建线程
在 POSIX 线程库中,使用 pthread_create 函数来创建线程。该函数的原型如下:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
thread:指向线程标识符的指针。
attr:用于设置线程属性,通常为 NULL,表示使用默认属性。
start_routine:线程函数的起始地址,线程创建后会执行该函数。
arg:传递给线程函数的参数。
下面是一个简单的线程创建示例:
#include <stdio.h>
#include <pthread.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_id;
int ret = pthread_create(&thread_id, NULL, thread_function, NULL);
if (ret != 0) {
printf("Thread creation failed!\n");
return 1;
}
printf("Main thread is running...\n");
pthread_join(thread_id, NULL); // 等待线程结束
printf("Main thread is exiting...\n");
return 0;
}
运行结果:

在这个示例中,主线程创建了一个新线程,新线程执行 thread_function 函数。主线程通过 pthread_join 函数等待新线程结束。
3.1.2 终止线程
线程可以通过以下方式终止:
- 正常返回:线程函数执行完毕并返回,线程自动终止。
- 调用
pthread_exit:线程可以调用 pthread_exit 函数主动终止自己。
- 被其他线程取消:其他线程可以调用
pthread_cancel 函数取消指定线程。
下面是一个使用 pthread_exit 终止线程的示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* thread_function(void* arg)
{
printf("Thread is running...\n");
sleep(2);
printf("Thread is exiting...\n");
pthread_exit(NULL); // 主动终止线程
}
int main()
{
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, thread_function, NULL);
if (ret != 0) {
printf("Thread creation failed!\n");
return 1;
}
printf("Main thread is running...\n");
pthread_join(thread_id, NULL); // 等待线程结束
printf("Main thread is exiting...\n");
return 0;
}
运行结果:

3.2 线程的属性
线程的属性可以通过 pthread_attr_t 结构体来设置。常见的线程属性包括:
- 线程的分离状态:线程可以是可连接的(joinable)或分离的(detached)。可连接的线程在终止后需要其他线程调用
pthread_join 来回收资源,而分离的线程在终止后会自动释放资源。
- 线程的栈大小:可以设置线程的栈大小。
- 线程的调度策略:可以设置线程的调度策略(如 FIFO、轮转等)。
下面是一个设置线程属性的示例:
#include <stdio.h>
#include <pthread.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_id;
pthread_attr_t attr;
pthread_attr_init(&attr); // 初始化线程属性
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置线程为分离状态
int ret = pthread_create(&thread_id, &attr, thread_function, NULL);
if (ret != 0) {
printf("Thread creation failed!\n");
return 1;
}
printf("Main thread is running...\n");
sleep(3); // 主线程等待一段时间,确保子线程执行完毕
printf("Main thread is exiting...\n");
pthread_attr_destroy(&attr); // 销毁线程属性
return 0;
}
运行结果:

在这个示例中,我们通过 pthread_attr_setdetachstate 函数将线程设置为分离状态,这样线程在终止后会自动释放资源,主线程不需要调用 pthread_join 来等待线程结束。
3.3 线程的取消
线程可以通过 pthread_cancel 函数来取消。被取消的线程会在下一个取消点(cancellation point)终止。取消点通常是某些系统调用或库函数,如 sleep、read、write 等。
下面是一个线程取消的示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* thread_function(void* arg)
{
printf("Thread is running...\n");
while (1) {
printf("Thread is working...\n");
sleep(1);
}
return NULL;
}
int main()
{
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, thread_function, NULL);
if (ret != 0) {
printf("Thread creation failed!\n");
return 1;
}
sleep(3); // 主线程等待3秒
pthread_cancel(thread_id); // 取消线程
printf("Thread has been canceled.\n");
pthread_join(thread_id, NULL); // 等待线程结束
printf("Main thread is exiting...\n");
return 0;
}
运行结果:

在这个示例中,主线程在 3 秒后取消了子线程,子线程在 sleep 函数处被取消。
3.4 线程的清理
线程在终止时可能需要执行一些清理操作,如释放资源、关闭文件等。POSIX 线程库提供了 pthread_cleanup_push 和 pthread_cleanup_pop 函数来注册和注销清理函数。
下面是一个线程清理的示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void cleanup_function(void* arg)
{
printf("Cleanup function is called: %s\n", (char*)arg);
}
void* thread_function(void* arg)
{
pthread_cleanup_push(cleanup_function, "Resource 1");
pthread_cleanup_push(cleanup_function, "Resource 2");
printf("Thread is running...\n");
sleep(2);
printf("Thread is exiting...\n");
pthread_cleanup_pop(1); // 执行清理函数
pthread_cleanup_pop(1); // 执行清理函数
return NULL;
}
int main()
{
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, thread_function, NULL);
if (ret != 0) {
printf("Thread creation failed!\n");
return 1;
}
pthread_join(thread_id, NULL); // 等待线程结束
printf("Main thread is exiting...\n");
return 0;
}
在这个示例中,我们使用 pthread_cleanup_push 注册了两个清理函数,当线程终止时,这些清理函数会被自动调用。
四、线程分离
4.1 什么是线程分离?
线程分离(Detached Thread)是指线程在终止后自动释放其资源,而不需要其他线程调用 pthread_join 来回收资源。分离线程通常用于不需要返回结果的场景。
4.2 设置线程为分离状态
可以通过 pthread_detach 函数将线程设置为分离状态。下面是一个线程分离的示例:
#include <stdio.h>
#include <pthread.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_id;
int ret = pthread_create(&thread_id, NULL, thread_function, NULL);
if (ret != 0) {
printf("Thread creation failed!\n");
return 1;
}
pthread_detach(thread_id); // 将线程设置为分离状态
printf("Main thread is running...\n");
sleep(3); // 主线程等待一段时间,确保子线程执行完毕
printf("Main thread is exiting...\n");
return 0;
}
在这个示例中,我们通过 pthread_detach 函数将线程设置为分离状态。分离状态的线程在终止后会自动释放资源,主线程不需要调用 pthread_join 来等待线程结束。
4.3 线程分离的注意事项
- 资源释放:分离线程在终止时会自动释放资源,因此不需要调用
pthread_join 来回收资源。
- 无法获取返回值:分离线程的返回值无法被其他线程获取,因为线程终止后资源已经被释放。
- 线程状态:一旦线程被设置为分离状态,就不能再通过
pthread_join 来等待线程结束。
五、线程的调度
5.1 线程的调度策略
Linux 中的线程调度策略主要有以下几种:
- SCHED_FIFO:先进先出调度策略,优先级高的线程会一直运行,直到它主动放弃 CPU。
- SCHED_RR:轮转调度策略,优先级高的线程会运行一段时间(时间片),然后让出 CPU 给其他相同优先级的线程。
- SCHED_OTHER:默认的调度策略,基于时间片的动态优先级调度。
5.2 设置线程的调度策略
可以通过 pthread_setschedparam 函数来设置线程的调度策略和优先级。下面是一个设置线程调度策略的示例:
#include <stdio.h>
#include <pthread.h>
#include <sched.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_id;
pthread_attr_t attr;
struct sched_param param;
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO); // 设置调度策略为FIFO
param.sched_priority = 50; // 设置优先级
pthread_attr_setschedparam(&attr, ¶m);
int ret = pthread_create(&thread_id, &attr, thread_function, NULL);
if (ret != 0) {
printf("Thread creation failed!\n");
return 1;
}
pthread_join(thread_id, NULL); // 等待线程结束
printf("Main thread is exiting...\n");
pthread_attr_destroy(&attr); // 销毁线程属性
return 0;
}
运行结果:

在这个示例中,我们通过 pthread_attr_setschedpolicy 函数将线程的调度策略设置为 SCHED_FIFO,并通过 pthread_attr_setschedparam 函数设置了线程的优先级。
六、总结
本文详细介绍了 Linux 中线程的基本概念和线程控制的相关知识,包括线程的创建与终止、线程属性、线程的取消与清理、线程的调度等内容。通过代码示例,读者可以更好地理解这些概念,并在实际编程中应用这些知识。
在理解线程的过程中,建议把“进程”和“线程”的关系反复梳理清楚:进程负责资源分配,线程负责执行流。这个认知对于后续学习线程同步、互斥锁、条件变量等内容至关重要。如果对 多线程编程 在服务端开发中的实际应用感兴趣,不妨继续深入研究 操作系统 的调度机制与内存管理原理,二者结合会让你对并发模型有更完整的把握。