CPU核数跟多线程的关系

一直以来有这样的疑惑,单核CPU适合多线程吗?是不是几个核的CPU开几个线程是最合适的?

今天就这一问题查了一些资料,现整理如下:

    要说多线程就离不开进程,进程和线程的区别在这里就不详细说了,只将关键的几点:

a)进程之间是相互独立的,不共享内存和数据,线程之间的内存和数据是公用的,每个线程只有自己的一组CPU指令、寄存器和堆栈,对于线程来说只有CPU里的东西是自己独享的,程序中的其他东西都是跟同一个进程里的其他线程共享的。

b)操作系统创建进程时要分配好多外部资源,所以开销大。(这个跟操作系统有关,有人做过实验,window创建进程的开销大,linux创建进程的开销就很小。)

    再来说一下CPU,在过去单CPU时代,单任务在一个时间点只能执行单一程序。之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程。虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行。而现在多核CPU的情况下,同一时间点可以执行多个任务,具体到这个任务在CPU哪个核上运行,这个就跟操作系统和CPU本身的设计相关了。

    我们假设一个极端的情况:在一台单核计算机上只运行2个程序,一个是我们的程序A,另一个是操作系统的程序B,每个程序是一个进程。单核CPU的时候,A和B在CPU上交替运行,具体的分配方式由操作系统来判断,我这里猜测应该跟A和B的线程数有关,因为线程是CPU级别的,如果A有5个线程,B也有5个线程,那么CPU分配给A和B的时间应该是1:1的;如果A增加到15个线程,CPU分配给A和B的时间应该是3:1的比例。所以此时如果A的线程数多,那么获得的CPU执行次数就多,处理的速度也就快了。以上假设的前提是:①A和B的优先级相同,②A和B都是只消耗CPU资源的程序。

如果相同的情况用一个双核的计算机来处理又会是什么结果呢?假设这个双核的计算机和操作系统比较傻,把A进程分配到核1上,B进程分配到核2上,那不管A有几个线程,都是用核1来处理,那么时间肯定是一样的。不过现实中应该不会有这么傻的CPU和操作系统吧。偷笑所以赶紧还是会根据线程来进行处理,当A的线程比B多时,会占用核2来处理A的线程。

    刚才说的是只消耗CPU资源的程序,但这样的程序在实际应用中基本上是没有的,总会有跟资源打交道的。比如读个文件,查个数据库,访问一个网络连接等等。这个时候多线程才真正体现出优势,在一个进程中,线程a去读文件,线程b去查数据库,线程c去访问网络,a先用一下CPU,然后去读文件,此时CPU空闲,b就用一下,然后去查数据库,……,相对于读文件、查数据库、访问网络来说CPU计算的时间几乎可以忽略不计,所以多线程实际上是计算机多种资源的并行运用,跟CPU有几个核心是没什么关系的。






### Python多线程编程中获取CPU的方法 在Python中,可以通过多种方式获取当前系统的CPU量。以下是几种常见的方法: #### 方法一:使用 `multiprocessing` 模块 `multiprocessing` 是Python标准库的一部分,提供了用于跨平台并行处理的功能。其中的 `cpu_count()` 函可以直接返回系统中的逻辑CPU。 ```python import multiprocessing cores = multiprocessing.cpu_count() print(f"可用 CPU :{cores}") ``` 这种方法简单易用,适合大多场景[^1]。 #### 方法二:通过环境变量获取 某些情况下,可能需要手动设置或读取环境变量来控制程序使用的CPU。例如,在科学计算领域常用的NumPy、SciPy等库会依赖OpenMP或多线程BLAS库(如Intel MKL),此时可以设置环境变量 `OMP_NUM_THREADS` 和 `MKL_NUM_THREADS` 来指定线程。 ```bash export OMP_NUM_THREADS=$(nproc) export MKL_NUM_THREADS=$(nproc) ``` 在脚本内部也可以动态读取这些环境变量: ```python import os omp_threads = int(os.getenv('OMP_NUM_THREADS', multiprocessing.cpu_count())) mkl_threads = int(os.getenv('MKL_NUM_THREADS', multiprocessing.cpu_count())) print(f"OMP_NUM_THREADS: {omp_threads}, MKL_NUM_THREADS: {mkl_threads}") ``` 这种方式特别适用于高性能计算环境中对资源的精细管理[^1]。 #### 方法三:借助第三方库 `psutil` 如果需要更详细的硬件信息,可以考虑安装并使用 `psutil` 库。它不仅能够提供CPU,还能区分物理心和逻辑心。 ```python import psutil physical_cores = psutil.cpu_count(logical=False) # 只统计物理心 logical_cores = psutil.cpu_count(logical=True) # 统计所有逻辑心 print(f"物理:{physical_cores},逻辑:{logical_cores}") ``` 注意,`psutil` 并不在Python的标准库中,需单独安装 (`pip install psutil`)。但对于复杂需求来说,这是一个非常大的工具[^2]。 --- ### 关于Python多线程CPU关系 尽管可以从技术层面轻松获得CPU目,但由于 **全局解释器锁 (Global Interpreter Lock, GIL)** 的存在,Python的多线程并不适合作为提高CPU密集型任务性能的主要手段。具体而言,GIL允许同一时刻只有一个原生线程执行Python字节码,这使得即使有多个CPU心也无法实现真正的并行运算[^3]。 然而,对于I/O密集型任务(比如等待网络响应或者磁盘操作完成的任务),多线程仍然有效果,因为在这种情况下线程会在等待期间释放GIL给其他线程运行的机会。 为了充分利用多处理器的能力,推荐采用以下策略之一: - 对于CPU密集型任务,改用基于多进程的技术,例如 `concurrent.futures.ProcessPoolExecutor` 或者直接使用 `multiprocessing` 模块; - 利用支持多线程且不受GIL影响的扩展库,像Cython编译后的代码片段、Numba JIT加速函等。 --- ### 示例代码展示 下面是一个简单的例子,演示如何根据CPU启动相应量的工作进程来进行批量据处理: ```python from concurrent.futures import ProcessPoolExecutor import multiprocessing def cpu_bound_task(x): """模拟一个耗时的CPU密集型任务""" return sum(i * i for i in range(10_000)) if __name__ == "__main__": cores = multiprocessing.cpu_count() with ProcessPoolExecutor(max_workers=cores) as executor: inputs = list(range(cores)) results = list(executor.map(cpu_bound_task, inputs)) print("Results:", results[:5]) # 打印部分结果验证正确性 ``` ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值