1. fork返回两次的原因:调用fork时,操作系统把当前进程复制了一份,然后fork在父子进程分别返回了一次。
2. python的os模块内的fork只能在linux类系统上调用:
2.1 import os
2.2 pid = os.fork() # pid在子进程中为0;在父进程中为子进程的进程id。
3. multiprocessing模块
3.1 multiprocessing是跨平台版本的多进程模块。
3.2 创建子进程示例:
from multiprocessing import Process # 从multiprocessing模块导入Process类。
pid = Process(target = childFunc, args = ('test', )) # 创建一个Process实例,即表示创建了一个子进程。target成员为子进程的执行函数;args成员为参数。
pid.start() # 用于启动子进程。
pid.join() # 等待子进程结束。通常用于进程间的同步。
3.3 通过进程池创建子进程示例:
from multiprocessing import Pool # 从multiprocessing模块导入Pool类。
pids = Pool(4) # 创建最多拥有四个进程的进程池
for loop in range(4);
pids.apply_async(childFunc, args = (loop, ))
pids.close() # 调用join()前必须先调用close()。
pids.join() # 等待所有子进程执行结束。
4. subprocess模块
4.1 类似于linux上的system()系统调用。
5. 进程间通信
5.1 multiprocessing模块提供了Queue和Pipes等多种方式支持进程间通信。
5.2 通过Queue的put()方法向队列中写数据;通过get()方法读出队列中的数据。
1. python标准库提供两个线程模块:_thread和threading。_thread是低级模块;threading是高级模块,封装自_thread模块;一般使用threading模块。
2. 启动一个线程就是创建一个Thread类的实例,并调用实例的start()方法开始执行。示例:
import threading
td = threading.Thread(target = threadFunc, name = 'testThread', args = (4, ))
td.start()
3. threading模块的current_thread()方法用来返回当前线程的实例。threading.current_thread().name是当前线程的名字。
4. Lock
4.1 注意:高级语言的一条语句在CPU执行时是若干条语句。
4.2 全局变量在多线程间是共享的,修改变量前需要加锁。示例:
lock = threading.Lock() # 创建Lock类的实例
lock.acquire() # 获取锁
try:
do something
finally:
lock.release()
5. 多核CPU:
5.1 GIL锁:Global Interpreter Lock全局解释器锁。
5.2 每个进程拥有一个GIL锁。
5.3 官方CPython解释器在执行python线程前需要首先获得GIL锁,执行100条字节码后释放GIL锁,其他线程获取锁后依次执行。因此python线程不能真正利用多核。
5.4 python真正利用多核只能使用进程。
1. 用法:创建threading模块下local类的实例,该实例变量作为全局变量。
2. 一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本。
1. 多进程优点:稳定性相对高,子进程异常不会影响父进程或其他子进程。
2. 多进程缺点:创建进程开销大。
3. 计算密集型 vs IO密集型
3.1 计算密集型:消耗CPU资源。任务数越多,任务切换带来的CPU消耗越大,因此建议任务数为CPU核心数;建议使用C语言编写;
3.2 IO密集型:消耗CPU资源少,IO消耗时间长,因此任务数相对多些,CPU的利用率会高;适合使用开发效率高的语言,如Python。
4. 异步IO:
4.1 称为事件驱动模型。单进程单线程模型也能够支持多任务。
4.2 Nginx是支持异步IO的web服务器。
4.3 Python语言的异步编程模型称为协程。
1. multiprocessing模块不但支持多进程,其子模块managers还支持把多进程分布到多态机器上。
2. Queue的作用是传递任务和接收结果,每个任务的描述数据量要尽量小。