Linux线程概念
- 线程是程序内部的执行路线,Linux中也称为轻量级进程。
- 线程在进程地址空间内运行,共享大部分资源,如虚拟地址空间和页表。
二级页表:
- 二级页表是虚拟内存管理的一种技术,用于将虚拟地址映射到物理内存地址。
- 在32位平台下,使用页目录和页表的二级页表来实现地址映射。
- 二级页表允许将大型地址空间划分成较小的单元,减少内存开销。
线程的优点:
- 线程可以在同一进程内并发执行,提高多核处理器的利用率。
- 线程之间共享内存,减少通信和同步的开销。
- 轻量级,创建和销毁线程比进程更快。
线程的缺点:
- 多线程编程复杂,容易引发竞态条件和死锁。
- 线程之间共享内存可能导致数据一致性问题。
- 线程调度和同步开销较大。
线程异常:
- 线程可能面临异常,如分段错误(段错误),当尝试修改只读内存时会导致段错误。
- 操作系统会终止触发异常的线程,通常通过信号来处理异常。
线程用途:
- 线程用于多任务并发执行,如服务器、多媒体处理、并行计算等。
- 线程可用于提高应用程序性能和响应性。
Linux进程VS线程:
- 进程是程序的独立执行实体,拥有独立的地址空间和资源。
- 线程是进程内的执行单元,共享进程的地址空间和资源。
Linux线程控制:
- 原生线程库pthread提供线程管理接口。
- 线程创建、等待、终止、分离等基本操作。
- 线程ID是用于标识线程的唯一标识符。
线程的优点2:
- 创建一个新线程的代价比创建一个新进程小。
- 线程切换需要的操作系统工作较少,相比进程切换更轻量。
- 线程占用的资源较少,相比进程更节省。
- 可以充分利用多处理器的可并行数量,提高性能。
- 在等待慢速IO操作完成时,程序可以执行其他计算任务,提高效率。
- 对于计算密集型应用,可以将计算任务分解到多个线程中以在多处理器系统上运行。
- 对于IO密集型应用,可以重叠IO操作,线程可以同时等待不同的IO操作,提高性能。
线程的缺点2:
- 性能损失:计算密集型线程可能无法充分共享处理器,导致性能损失。
- 健壮性降低:多线程编程需要更全面的考虑,可能因时间分配或共享变量而导致问题。
- 缺乏访问控制:线程内部调用某些OS函数可能对整个进程产生影响。
- 编程难度增加:编写和调试多线程程序比单线程程序更复杂。
线程异常:
- 单个线程出现异常(如除零或野指针),会导致线程崩溃,进而触发信号机制,终止进程。
线程用途:
- 多线程可提高CPU密集型程序的执行效率。
- 多线程可提高IO密集型程序的用户体验,例如同时进行下载和开发。
Linux进程VS线程:
- 进程是承担分配系统资源的基本实体,线程是调度的基本单位。
- 线程共享进程数据,但也有自己的一部分数据,如线程ID、寄存器、栈、errno等。
- 进程内的多个线程共享代码段和数据段。
- 主线程可以创建其他子线程。
- Linux中,应用层的线程与内核的LWP(轻量级进程)一一对应,操作系统使用LWP进行调度。
线程控制
- POSIX线程库(pthread)是Linux下的原生线程库。
- 使用pthread_create函数创建新线程,传入线程ID、属性、线程例程函数和参数。
- 主线程创建的新线程可以并发运行。
获取线程ID:
- 通过创建线程时的输出型参数获得线程ID。
- 通过调用
pthread_self
函数获得当前线程的ID,类似于获取进程ID的getpid
函数。
线程等待:
- 线程需要被等待以释放其资源,否则可能导致内存泄漏。
- 使用
pthread_join
函数等待线程的结束。 pthread_join
函数的参数包括被等待线程的ID和线程退出时的退出码信息。- 线程等待成功返回0,失败返回错误码。
线程终止:
- 线程可以通过以下方式终止:
- 从线程函数中使用
return
语句退出,此时退出码可用于判断线程的正常运行结果。 - 调用
pthread_exit
函数主动终止线程,同时指定退出码。 - 一个线程可以调用
pthread_cancel
函数终止同一进程中的另一个线程。
- 从线程函数中使用
- 线程使用
return
退出时,主线程退出会导致整个进程退出,资源被释放。 - 使用
pthread_exit
函数可以主动终止线程并设置退出码。
需要注意的细节:
pthread_join
函数默认以阻塞方式等待线程结束。- 线程异常终止会导致整个进程崩溃,因此线程的健壮性较弱。
pthread_self
函数获取的线程ID与内核的轻量级进程ID(LWP)不一定相等,它们有一对一的关系。pthread_join
只能获取到线程正常退出时的退出码,无法获取线程异常退出的信息。- 调用
pthread_exit
或return
退出线程时,指定的退出码所指向的内存单元必须是全局的或使用malloc
分配的,而不是线程函数栈上的。
pthread_cancel
函数是用于取消线程的,以下是关于 pthread_cancel
函数的功能、用法、返回值、参数意义和需要注意的细节的总结:
功能: pthread_cancel
函数用于取消指定线程的执行。
用法:
int pthread_cancel(pthread_t thread);
thread
:被取消线程的ID。
返回值:
- 线程取消成功返回0,失败返回错误码。
参数意义:
thread
:要取消的线程的ID。
需要注意的细节:
- 线程是可以取消的,包括可以自己取消自己。但通常情况下,线程控制是由主线程或其他线程来取消目标线程。
- 当线程被成功取消后,它的退出码通常会变成-1,而不是原来设定的退出码。
- 在示例代码中,通过
pthread_cancel(pthread_self())
取消了线程的自身执行,这通常不是推荐的做法。 - 新线程也可以取消主线程,但这不是一种推荐的做法。通常情况下,我们使用主线程来控制其他新线程。
- 可以使用
pthread_detach
函数将线程分离,这样在线程退出后,系统会自动回收线程资源,无需显式调用pthread_join
。 pthread_t
类型的线程ID本质上是进程地址空间中共享区域的虚拟地址,用来唯一标识每个线程。不同的线程拥有不同的虚拟地址,这使得可以区分每个线程。- 使用
pthread_self()
函数获取线程ID,它实际上返回一个虚拟地址,用于唯一标识当前线程。 pthread_cancel
取决于线程库的实现,但在Linux的NPTL线程库中,线程ID本质上是共享区域上的虚拟地址。
函数总结
pthread_create
函数
- 原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
- 功能:
pthread_create
用于创建新线程,并在新线程中执行指定的函数start_routine
。- 参数意义:
thread
:指向线程ID的指针,用于存储新线程的ID。attr
:线程属性,通常设置为NULL
,表示使用默认属性。start_routine
:线程执行的函数,这个函数接受一个void*
参数,可以用来传递数据给新线程。arg
:传递给start_routine
函数的参数。- 返回值:
- 如果成功创建线程,返回0。
- 如果出现错误,返回相应的错误码。
- 注意事项:
- 线程创建成功后,新线程会执行
start_routine
函数,可以通过pthread_join
或pthread_detach
来回收线程资源。pthread_create
创建的线程默认是可连接(joinable)的,需要调用pthread_join
来等待线程的结束并回收资源。
pthread_cancel
函数
- 原型:
int pthread_cancel(pthread_t thread);
- 功能:
pthread_cancel
用于取消指定线程的执行。- 参数意义:
thread
:被取消线程的ID。- 返回值:
- 线程取消成功返回0,失败返回错误码。
- 注意事项:
- 线程可以被自己或其他线程取消,但通常建议由主线程或其他线程来取消目标线程。
- 当线程被成功取消后,它的退出码通常会变成-1,而不是原来设定的退出码。
pthread_detach
函数
- 原型:
int pthread_detach(pthread_t thread);
- 功能:
pthread_detach
用于将线程分离,使其在退出后自动回收资源,无需显式调用pthread_join
。- 参数意义:
thread
:要分离的线程的ID。- 返回值:
- 线程分离成功返回0,失败返回错误码。
- 注意事项:
- 分离线程后,线程退出时系统会自动回收线程资源,无需再调用
pthread_join
。- 分离和可连接(joinable)是冲突的,同一线程不能同时被分离和设置为可连接。
pthread_self
函数
- 原型:
pthread_t pthread_self(void);
- 功能:
pthread_self
用于获取当前线程的线程ID。- 参数意义:
- 无参数。
- 返回值:
- 返回调用线程的线程ID,通常是一个虚拟地址,用于唯一标识当前线程。
- 注意事项:
pthread_self
返回的线程ID用于标识当前线程,通常不需要显式创建,而是由线程库自动分配。
pthread_join
函数
- 原型:
int pthread_join(pthread_t thread, void **retval);
- 功能:
pthread_join
用于等待指定线程的结束,并获取其退出状态。- 参数意义:
thread
:要等待的线程的ID。retval
:一个指向指针的指针,用于存储被等待线程的退出状态。- 返回值:
- 如果成功等待线程的结束,返回0。
- 如果出现错误,返回相应的错误码。
- 注意事项:
pthread_join
会阻塞调用线程,直到指定线程结束为止。- 被等待线程的退出状态将被存储在
retval
指向的内存中,可以通过**retval
来获取退出状态。- 如果不关心线程的退出状态,可以将
retval
设置为NULL
。- 如果线程已经被分离,或者已经被另一个线程等待,再次调用
pthread_join
将导致错误。- 线程在退出时可以通过
pthread_exit
函数设置退出状态,退出状态通常是一个整数值,可以传递给其他线程以进行处理。