并发基础之线程

线程的构成

线程是操作系统调度的最小执行单位,它通常由以下几个基本组成部分构成。

线程ID(Thread ID)

每个线程都有一个唯一的标识符,称为线程ID。线程ID用于在操作系统中唯一地标识和区分不同的线程。

程序计数器(Program Counter)

程序计数器是一个指向线程当前执行指令的指针。它指示了线程执行的位置,当线程被中断或切换时,程序计数器会保存当前指令的地址,以便在恢复执行时继续从该指令处执行。

寄存器集合(Register Set)

线程具有自己的寄存器集合,包括通用寄存器、栈指针、堆指针等。这些寄存器用于保存线程的上下文信息,包括局部变量、函数调用参数和返回地址等。在线程切换时,寄存器集合中的值被保存和恢复,以确保线程的状态得以正确维护。

栈(Stack)

每个线程都有自己的栈空间,用于存储局部变量、函数调用信息和临时数据。栈是线程独立的,每个线程有自己的栈帧。栈的大小和生命周期由线程创建时的参数或操作系统的设置决定。

线程状态(Thread State)

线程可以处于不同的状态,如运行态、就绪态、阻塞态等。线程状态表示线程当前所处的运行状态,操作系统根据线程状态进行调度和管理。

线程优先级(Thread Priority)

线程优先级用于确定线程在竞争系统资源时的调度顺序。高优先级的线程在竞争资源时更有可能被优先执行。线程优先级通常由操作系统或线程创建时的参数指定。

特点

轻量性

线程相对于进程而言是轻量级的,创建和切换线程的开销比创建和切换进程的开销要小得多。因此,线程的创建和销毁相对较快,可以更高效地利用系统资源。

共享地址空间

线程在同一进程内共享相同的地址空间和资源。这意味着线程之间可以直接访问共享内存,使得数据共享和通信更加方便和高效。

并发执行

多个线程可以并发执行,即在同一时间段内同时运行。通过在不同的线程中执行不同的任务,可以实现并发处理和提高系统的吞吐量和响应能力。

上下文切换

在多线程环境中,操作系统会根据调度策略在不同的线程之间进行切换。上下文切换是指保存当前线程的上下文信息,并加载下一个线程的上下文信息,使得线程能够恢复执行。上下文切换的开销较大,因此,过多的线程切换可能会影响系统性能。

共享资源的竞争

由于线程之间共享资源,可能会导致竞争条件的出现。竞争条件指的是当多个线程同时访问和修改共享资源时,可能出现不确定的结果。为了避免竞争条件,需要采用同步机制(如锁、信号量等)来保护共享资源的访问。

独立的执行路径

每个线程都有独立的执行路径,它可以独立地执行特定的任务或代码段。线程之间可以并行执行不同的操作,从而实现任务的分解和并发执行。

分类

线程可以根据其用途和执行方式分为以下几种常见的类型。

用户线程(User Thread)

用户线程是由应用程序开发者创建和管理的线程。它们在用户空间中运行,不依赖于操作系统的线程实现。用户线程的创建、调度和同步等操作由应用程序自己负责,而不涉及操作系统的干预。用户线程的优点是灵活性高,但缺点是无法利用多核处理器的并行能力。

内核线程(Kernel Thread)

内核线程是由操作系统内核创建和管理的线程。它们在内核空间中运行,可以直接调用和利用操作系统提供的线程管理功能。内核线程的创建、调度和同步等操作由操作系统负责,应用程序无需关心具体实现细节。内核线程的优点是稳定性高,可以充分利用多核处理器的并行能力。

轻量级进程(Lightweight Process,LWP)

轻量级进程是一种由操作系统内核提供的线程模型,它在应用程序和内核线程之间建立了一个中间层。轻量级进程可以看作是内核线程和用户线程之间的折中方案,它具备用户线程的灵活性和内核线程的稳定性。轻量级进程通常由操作系统内核管理,应用程序可以创建和控制它们,但内核线程的创建和调度仍由操作系统负责。

守护线程(Daemon Thread)

守护线程是在后台运行的线程,它的存在不会阻止程序的终止。当所有的非守护线程结束时,守护线程会自动终止。守护线程通常被用于执行一些后台任务,如垃圾回收、日志记录等。

常见的应用场景

并发任务处理

线程可以用于同时执行多个任务,实现并发处理。例如,一个网络服务器可以使用多个线程同时处理多个客户端请求,提高系统的响应能力和吞吐量。

图形用户界面(GUI)应用程序

GUI应用程序通常需要响应用户的输入和同时进行其他操作,如更新界面、处理后台任务等。通过将用户交互和后台任务处理放在不同的线程中,可以提高界面的响应性,避免阻塞用户界面。

多媒体处理

多媒体应用程序(如音频、视频处理等)通常需要实时性能和高并发处理能力。使用多线程技术可以将不同的处理任务分配给不同的线程,实现并行处理,提高处理速度和效率。

数据库操作

在数据库应用中,线程可以用于并发执行数据库查询、更新和事务操作。通过将不同的数据库操作放在不同的线程中,可以提高数据库的并发性和性能。

多线程编程模型

某些应用场景需要采用多线程编程模型,例如并行计算、科学模拟、数据分析等。通过使用多个线程,可以将任务分解为更小的子任务,并在多个线程上并行执行,加快计算速度和提高系统的效率。

异步编程

线程还常用于实现异步编程模型,例如在网络通信中进行异步请求和响应处理,或在事件驱动的程序中处理异步事件。通过使用线程来处理异步任务,可以避免阻塞主线程,提高程序的并发性和响应能力。

Python线程

简介

在 Python 中,线程的时间片机制由解释器的全局解释器锁(Global Interpreter Lock,GIL)控制。GIL 是一种机制,它确保在任何给定的时间点上只有一个线程在解释器中执行 Python 字节码。

由于 GIL 的存在,Python 的多线程并不是真正的并行执行,而是通过线程切换的方式实现并发。GIL 会在一段时间内允许一个线程执行,然后释放 GIL,让其他线程有机会执行。这种线程切换是由解释器在字节码级别进行的,并且通常发生在 I/O 操作或者执行时间较长的计算操作之间。

由于 GIL 的存在,Python 的多线程在 CPU 密集型任务上并不能充分利用多核处理器的并行能力。然而,在 I/O 密集型任务中,由于线程在等待 I/O 操作完成时会释放 GIL,所以多线程可以提供一定的性能提升。

需要注意的是,GIL 只存在于 CPython 解释器中,而其他实现如 Jython 和 IronPython 并没有 GIL。另外,对于 CPU 密集型任务,可以考虑使用多进程或其他并发模型(如协程)来绕过 GIL,实现真正的并行执行。

在 Python 中,如果需要并行执行 CPU 密集型任务,可以使用多进程库(如 multiprocessing)来创建多个进程,并在每个进程中运行独立的 Python 解释器,从而绕过 GIL,实现并行执行。对于 I/O 密集型任务,多线程通常可以提供较好的性能,因为线程在等待 I/O 的时候会释放 GIL,让其他线程有机会执行。

线程数的设置

如何分配线程?依据CPU的占用率,我们可以得出一个公式:
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
备注这个公式也是前辈们分享的,当然之前看了淘宝前台系统优化实践的文章,和上面的公式很类似,不过在CPU数目那边,他们更细化了,上面的公式只是参考。不过不管什么公式,最终还是在生产环境中运行后,再优化调整。
我们继续上面的任务,我们的服务器CPU核数为4核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:( 80 + 20 )/20 * 4 = 20。也就是设置20个线程数最佳。
从这个公式上面我们就得出,线程的等待时间越大,线程数就要设置越大,这个正好符合我们上面的分析,可提升CPU利用率。那从另一个角度上面说,线程数设置多大,是根据我们自身的业务的,需要自己去压力测试,设置一个合理的数值。

时间片

线程的时间片机制是操作系统用来调度和分配 CPU 时间给各个线程的一种策略。它确保每个线程都能在一定时间内获得CPU的执行时间,以实现并发执行。

时间片是操作系统划分给每个线程的最小时间单位。操作系统通过时间片轮转(Round-Robin)调度算法来分配时间片。具体来说,每个线程被分配一个时间片,在时间片用完之前,线程会一直占用 CPU 进行执行。当时间片用完后,操作系统会暂停当前线程的执行,并将 CPU 时间分配给下一个就绪状态的线程。

时间片的长度可以根据操作系统的调度策略和配置进行调整。通常,时间片的长度是相对较短的,以便操作系统能够更频繁地切换线程,实现多任务并发执行。

时间片机制的优点是能够公平地分配 CPU 时间给每个线程,确保每个线程都有机会执行。它也能够在多个线程之间实现快速切换,提高系统的响应能力和并发性。

然而,时间片机制也存在一些缺点。当线程数量较多时,频繁的线程切换会引入一定的开销,影响系统性能。此外,如果某个线程需要长时间执行,而时间片较短,就会导致频繁的线程切换,降低系统的效率。

需要注意的是,时间片机制是操作系统的一种调度策略,并且不同的操作系统可能有不同的实现方式和参数配置。

相关推荐

  1. 并发基础线

    2024-04-23 21:10:01       34 阅读
  2. 基础 | 并发编程 - [线状态]

    2024-04-23 21:10:01       43 阅读
  3. 并发线、进程基本概念

    2024-04-23 21:10:01       32 阅读
  4. 【QT进阶】Qt线并发线并发的简单介绍

    2024-04-23 21:10:01       30 阅读
  5. 并发编程:线同步基础:7、StampedLock DEMO

    2024-04-23 21:10:01       60 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-23 21:10:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-23 21:10:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-23 21:10:01       87 阅读
  4. Python语言-面向对象

    2024-04-23 21:10:01       96 阅读

热门阅读

  1. C/C++不定参函数

    2024-04-23 21:10:01       32 阅读
  2. flutter 点击按钮限流方案

    2024-04-23 21:10:01       26 阅读
  3. 大模型日报2024-04-23

    2024-04-23 21:10:01       58 阅读
  4. jupyter简要使用手册

    2024-04-23 21:10:01       36 阅读
  5. Nest.js学习记录4

    2024-04-23 21:10:01       31 阅读
  6. C#面:阐述什么是泛型委托

    2024-04-23 21:10:01       32 阅读
  7. React|创建txt文件并上传到oss指定地址

    2024-04-23 21:10:01       29 阅读
  8. 设备树中dtb和dtbo的区别

    2024-04-23 21:10:01       33 阅读
  9. 程序员缓解工作压力的小窍门

    2024-04-23 21:10:01       34 阅读