一、内核线程
先提一嘴什么是线程
线程(thread)是操作系统中能够进行运算调度的最小单位。它是被包含在进程之中的实际运作单位。每个进程可以包含多个线程,这些线程并发执行,每个线程负责执行不同的任务。线程共享进程的资源,如虚拟地址空间、文件描述符和信号处理等,但每个线程拥有自己独立的调用栈、寄存器环境和线程本地存储。
内核线程:是线程的一种类型,由操作系统内核直接创建和管理。内核线程运行在内核态,具有直接访问硬件和内核数据结构的能力。内核线程的主要职责是执行内核任务,如处理中断、管理内存页、执行文件系统操作等。由于内核线程运行在内核态,它们拥有更高的权限,可以执行一些用户线程无法执行的操作。
线程之间切换时(上下文切换)需要保留
-
寄存器(Registers):CPU寄存器中存储了线程当前的执行状态,如指令指针(IP/PC)、栈指针(SP)、数据寄存器等。当线程切换时,当前线程的寄存器状态需要被保存起来,以便之后能够正确恢复执行。
-
程序计数器(Program Counter, PC):程序计数器指示了CPU下一条要执行的指令的地址。在线程切换时,当前线程的程序计数器值需要被保存,以便线程恢复执行时能够从中断的地方继续执行。
-
栈区指针(Stack Pointer):栈区指针指向当前线程的调用栈的栈顶。调用栈中保存了函数的局部变量、返回地址等信息,对于线程的执行至关重要。因此,在线程切换时,需要保存当前线程的栈区指针。
保存栈区信息为何不保存堆区呢?
堆区是程序运行时动态分配内存的区域,它通常用于存储程序运行期间创建的对象。与栈区不同,堆区的数据不是线程私有的,多个线程可以共享堆区中的数据。因此,在线程上下文切换时,不需要保存堆区的状态。堆区的管理通常通过内存分配和释放函数(如malloc和free)进行,而不是通过线程的上下文切换来管理。
二、用户线程
用户线程与内核线程不同,是由用户程序创建的,极端可以理解为用户自己创建的线程,不与操作系统内核打交道,不依赖于操作系统的核心,其创建、同步、调度和管理等完全由用户空间的线程库函数来完成,无需内核的参与。
内核对用户线程的存在并不知情,仅看到的是作为整体运行的应用程序进程,用户应用程序里面有多少线程,操作系统是不知道的。
总结
内核线程运行在内核态,具有直接访问硬件和内核数据结构的能力。内核线程的主要职责是执行内核任务,如处理中断、管理内存页、执行文件系统操作等。由于内核线程运行在内核态,它们拥有更高的权限,可以执行一些用户线程无法执行的操作。
用户线程则是运行在用户态,不与操作系统内核打交道,不依赖于操作系统的核心,其创建、同步、调度和管理等完全由用户空间的线程库函数来完成,无需内核的参与。
由于用户线程的操作不需要陷入内核,在某些时候性能相对来说比较高。
内核线程调度全是由操作系统内部完成,而用户线程需要用户程序自己编写的调度逻辑
调度算法通常不如内核线程,不过用户库可以调整相应的调度策略,也不是绝对的。
Python中使用多线程和多进程的区别
一、 全局解释器锁(GIL):
Python有一个全局解释器锁(GIL),它限制了同一时间只能有一个线程执行Python字节码。这意味着,尽管Python支持多线程,但在CPU密集型任务中,多线程并不能实现真正的并行执行。相反,多进程可以充分利用多核CPU的优势,实现真正的并行执行。
二、资源共享方面的区别
在多线程环境中,所有的线程都共享同一个进程的资源(如内存空间、打开的文件描述符等)。这可以使得线程间的通信和数据共享变得相对简单,但也可能导致数据同步和线程安全问题,多进程环境中每个进程都有自己的独立资源空间,进程间的通信需要通过进程间通信(IPC)机制(如管道、共享内存、消息队列、信号量等)来实现,这增加了通信的复杂性,但提高了数据的安全性。
三、开销方面
线程的创建、销毁和切换的开销通常比进程小,因为线程是在进程内部共享资源的。这使得多线程在需要大量并发任务时可能具有更高的性能,但是因为有GIL锁的存在,多线程在CPU密集型任务中可能并不会带来性能提升。对于I/O密集型任务(如网络请求、文件读写等),由于GIL会在I/O操作期间释放,多线程可以有效地利用等待时间。
总之,由于GIL锁,所以通常在计算(cpu)密集型的场景下,如高计算性能,大规模处理和短时间响应的情况下,
使用多进程效果通常比多线程要好,反之,在IO密集型 如网络请求和文件读写等情况下,多线程效果通常要优于多进程