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)相关知识,包括线程的基本概念、线程标识、线程创建与回收、线程取消、线程终止以及线程分离。通过本章的学习,你应该能够掌握线程编程的基本技巧,并能够在实际项目中应用这些知识。
线程编程虽然复杂,但它是提高程序并发性和性能的重要手段。希望你能通过不断实践,深入理解线程的工作原理,并能够在实际项目中灵活运用。
加油!