多线程和多进程
通常在硬件层面上和操作系统层面上都存在线程的概念。但是这两个概念是完全不同的,是一个词汇在不同层面上的不同意思。
CPU数,核心数,硬件的线程数
-
CPU数指的是一个计算机主板上实际上卡槽中插入的CPU个数,由卡槽socket决定。一般的计算机是一个卡槽,因为多个卡槽的CPU之间共享内存等资源需要高级的技术。一般服务器是多个物理CPU的,也就是CUP数对于个人计算机是1,对于服务器是>1.
-
核心数指的是单个CPU的内部存在的可以同时处理的处理器。
- 这里首先强调是这里的核心是CPU内部存在一定的硬件支持的
- 第二是这里的同时指的是真正的同时处理,不同于操作系统层面上的同时可以是单个核心不断切换导致的同时。
-
线程数thread,逻辑处理器logical processor,或者虚拟核心virtual core,是同一个东西,指的是一个核心中可以存在着SMT(simultaneous multithreading)或者HT(hyper-threading)技术,这些技术可以使得单个核心同时执行多个线程,就相当于上文提到的单个核心数内部又存在多个子核心,总的核心是这些子核心数之和。
所以系统总的核心数实际上是上面所有的乘积。也就是逻辑处理器数量= CPU数* 核心数 * 超线程数(SMT or HT) 。
最后在硬件层面上其实线程和进程是一样的,指的就是几个核心或者存在超线程时候的总的线程数。
Linux查看以上信息的命令,lscpu
或者到/proc/cpuinfo
查看。
Windows可以直接看任务管理器。
操作系统层面的进程和线程
- 操作系统层面上(不严格讲)
- 一个线程指的是一串又有逻辑的指令,要先执行a,在执行b,等等。
- 一个进程简单理解就指的是一个程序,或者应用程序。
-
举例来说,打开一个应用程序,比如word,就会启动一个或者多个进程,每个进程执行一个任务,同时为了执行一个任务可能需要多个子任务,这些子任务就是线程。比如写入word时候需要自动保存也需要拼写检查,这就是同一个进程下的不同的线程。
-
操作系统层面的多进程和多线程指的是,基于单核没有超线程的CPU来说,操作系统会调度这些进程或者线程进行极短时间内的切换,以至于我们看不出来是切换的。所以任务是同时的。所以单核当CPU没办法实现真正的并行,单次或者每个时间只能进行一个线程或者进程,但是只要我切换的够快,时间就追不上我。还需要注意,一般我们使用程序数量远远多于计算机总的逻辑处理器数量,所以即便对于多核CPU来说,系统的切换也是极其重要的。
-
进程和线程本质是一个包含的关系,所以多进程和多线程机制是不同的,比如同一个进程内的多线程可以共享内存等等资源,通信也更加简单,每个线程需要自己的堆栈寄存器,所以占用资源少,多进程就是相对独立的,需要更多的内存等。所以分为多线程,多进程,和多线程多进程三种。多线程是指在一个进程中存在多个线程。
python的多进程(多线程)
python数值计算应该使用多进程的,对于计算密集型的,这样比较稳定稳定可靠,另外多线程对于有些变量会交错使用,导致结果容易错误。I/O密集型的大部分都在等待写入到硬盘时间, 对于I/O密集型的可以用多线程。另外python还有GIL。
多进程
-
利用multiprocessing中的Process实例,方法
from multiprocessing import Process p=Process(target=func,args=(arg,))#要进行多进程的程序需要写入到一个函数中。 p.start() p.join()#等待进程都结束,用于同步。
-
利用multiprocessing 中的Pool实现,启动大量的进程。
from multiprocessing import Pool p=Pool(4)#需要的logical 核心数量,一般和计算机有关。 def func(): pass for ii in range(5) p.apply_async(target=func,args=(arg,)) p.close()#在close之后就不能加入新的进程。 p.join()#用于等待所有进程结束,必须在close之后。
-
子进程之间的通信,利用queue.可以实现不同的进程写或者读取同一个结果。
from multiprocessing import Process, Queue import time def wirte(a): valus=['A','B','C'] for ii in valus: a.put(ii) time.sleep(5) def read(a): while True: q.get(True) q=Queue() pw=processing(target=,args=(q,)) pr=processing(target=,args=(q,)) pw.start() pr.start() pw.join() pr.join() or pr.terminate()#手动终止程序。
-
多进程的返回值
返回值初级用法
- join的作用是主程序等待这个程序结束继续执行p.join以后的内容,如果没有join则执行完程序不再执行以后的内容。
- 这里使用结束并行之后使用get或者多进程的返回值。
多线程
-
利用threading,方法和multiprocessing类似
import threading p=threading.Thread(target=,name='loop') p.start() p.join() threading.current_thread().name用于返回当前线程名字。
-
启动一个进程内的多线程时候, 都会首先建立一个主线程名字是Mainthread,接着会建立其他的子线程,名字默认是thread-1,Thead-2,或者是我们可以给定一个名字,比如上面所示,我们给定了一个loop的名字name.
-
该模块中又一个current_thread()的实例,用于返回当前线程。
-
多线程会有时候出错,例如。
一般来说用计算过程中多进程就够了。
python中qutip的并行
-
并行使用 qutip.parapallel.parfor,和 parallel.parallel_map,这两种函数进行并行。本质上qutip中的并行是借用了multiprocessing, 而且mcsolve方法中多条轨迹也是并行的,所以不能嵌套并行和mcsolve。
-
使用方法:首先定义一个函数该函数是要进行并行的函数,之后使用parfor传入函数和参数进行并行。
def func(x): return x**2 parfor(func,range(10)) ## parallwl_map(func,range(10))
-
这两个函数略有区别,返回的结果的顺序是不同的。(具体参考文档P127)
-
该函数可以输入的变量不限于数字。
-
该函数可以传入多个参数,这些参数的遍历方法不同,对于parfor和parallel有不同的遍历参数方法,同时支持传入任意关键词参数,但是不会用于计算。进行计算的仅仅是函数要求的那些。此外parfor的关键词不可以是num_cpus这是用于给定计算所需要的核心的。具体参考(128)
-
此外还可以加入progress_bar,只有parallel 有.
最后
Linux 并行嵌套会报错,子进程不能有守护进程。但是可以在循环之内写并行。
网友评论