1、什么是线程
线程是操作系统能够进行运算调度的最小单位,它是比进程更小的执行单元,存在于进程之内,是进程中的实际运作单位。一个进程可以包含一个或多个线程,每个线程都执行着进程代码段中的某个函数,但它们共享进程的大部分资源,如内存空间、打开的文件和全局变量,而每个线程拥有自己独立的调用栈、寄存器环境和线程本地存储(Thread Local Storage, TLS)。
线程的主要特点包括:
-
轻量化:线程相比于进程,切换成本更低,因为它们共享相同的地址空间,不需要像进程那样复制整个地址空间。
-
并发性:线程允许在同一进程中并行执行多个任务,这样可以利用多核处理器的并行计算能力,提高程序的执行效率。
-
通信便捷:由于线程共享进程资源,它们之间的通信比进程间通信更容易且开销更小。
-
独立调度:线程可以独立于其他线程被调度和执行。
-
生命周期:线程有其生命周期,包括创建、就绪、运行、阻塞和终止等状态。
根据线程的管理和调度方式,线程可以分为内核线程(Kernel Thread)、用户线程(User Thread)和混合线程(Hybrid Thread)。内核线程由操作系统内核直接管理和调度,用户线程则完全由用户级的线程库管理,而混合线程则是前两者的结合。
线程的类型包括:
- 主线程(Main Thread):是程序启动时操作系统创建的第一个线程。
- 子线程(Child Thread):是程序中创建的除主线程外的其他线程。
- 守护线程(Daemon Thread):在后台运行,为其他线程提供服务,当所有非守护线程结束时,守护线程也会自动结束。
- 前台线程(Foreground Thread):相对于守护线程,是指程序中执行主要功能的线程。
2、包含的头文件
#include <pthread.h>
3、常用函数及参数详解
3.1、创建线程函数pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
pthread_t *thread
: 指向一个pthread_t
类型的指针,用于存储新创建的线程的标识符。const pthread_attr_t *attr
: 线程属性对象的指针,可以用来设置线程的各种属性。如果为NULL
,则使用默认属性。void *(*start_routine)(void *)
: 线程函数的指针,即线程开始执行的函数地址。这个函数应该接受一个void *
类型的参数,并返回一个void *
类型的值。void *arg
: 传递给start_routine
函数的参数。
3.2、等待线程结束pthread_join()
int pthread_join(pthread_t thread, void **retval);
pthread_t thread
: 要等待的线程的标识符。void **retval
: 如果非NULL
,则用于存储线程函数的返回值。
3.3、终止线程执行pthread_exit
()
void pthread_exit(void *retval);
void *retval
: 线程函数的返回值,可以被其他线程通过pthread_join()
获取。
3.4、线程分离pthread_detach()
int pthread_detach(pthread_t thread);
参数thread
是要分离的线程的标识符。如果函数成功分离了线程,返回值为0;如果失败,则返回一个错误码。
3.5、代码示例
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// 线程函数,每个线程将执行此函数
void* thread_function(void* arg) {
int thread_num = *(int*)arg; // 获取线程编号
printf("Hello from thread %d\n", thread_num);
// 释放传入的参数,避免内存泄漏
free(arg);
// 线程执行完毕后调用 pthread_exit(),传递退出状态
pthread_exit(NULL);
}
int main() {
pthread_t threads[5]; // 创建一个数组来存储5个线程的ID
int *thread_ids[5]; // 创建一个数组来存储线程ID的指针,作为线程函数的参数
// 循环创建5个线程
for (int i = 0; i < 5; ++i) {
thread_ids[i] = malloc(sizeof(int)); // 为每个线程分配一个整数大小的内存
*thread_ids[i] = i + 1; // 设置线程编号
// 调用 pthread_create() 创建线程
// 参数1: 存储新线程ID的变量
// 参数2: 线程属性(可选,通常使用NULL表示使用默认属性)
// 参数3: 线程函数
// 参数4: 传给线程函数的参数
if (pthread_create(&threads[i], NULL, thread_function, thread_ids[i]) != 0) {
perror("Error creating thread");
exit(EXIT_FAILURE);
}
}
// 对前两个线程使用 pthread_join() 等待它们结束
for (int i = 0; i < 2; ++i) {
if (pthread_join(threads[i], NULL) != 0) {
perror("Error joining thread");
exit(EXIT_FAILURE);
}
}
// 对剩下的线程使用 pthread_detach() 进行分离,让它们独立运行
for (int i = 2; i < 5; ++i) {
if (pthread_detach(threads[i]) != 0) {
perror("Error detaching thread");
exit(EXIT_FAILURE);
}
}
printf("Main thread continues...\n");
return 0;
}
-
线程函数
thread_function()
: 每个线程将执行这个函数。它接受一个void*
类型的参数,通常用来传递任意类型的数据。在这个例子中,我们传递了一个指向整数的指针,代表线程的编号。 -
pthread_create()
: 用于创建新线程。它接收四个参数:新线程的ID(将被存储在第一个参数所指向的变量中)、线程属性(通常使用NULL表示使用默认属性)、线程函数的指针、以及传递给线程函数的参数。 -
pthread_join()
: 用于等待一个线程的结束。当主线程调用pthread_join()
并传入一个线程ID时,主线程将阻塞直到指定线程结束。 -
pthread_detach()
: 用于分离线程。分离后的线程将在没有主线程等待的情况下独立运行至结束。这意味着分离线程的资源将由系统自动回收,无需主线程显式调用pthread_join()
。 -
pthread_exit()
: 当线程执行完毕时调用,相当于线程的return
语句。在这个例子中,我们传递NULL
作为线程的退出状态,表示线程正常结束。