进程和线程
  • 多任务有三种方式:多进程、多线程、多进程加多线程
1、多进程
  • linux操作系统提供了一个fork()系统调用,调用一次,返回两次,因为**操作系统自动把当前进程复制了一份,然后分别在父进程和子进程内返回。**子进程返回0,父进程返回子进程id。子进程字需要调用getppid()就可以获得父进程的id。getpid()为获取当前进程id。
1.1、multiprocessing
  • 是跨平台版本的多进程模块
  • multiprocessing模块提供了一个Process类来代表一个进程对象。创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动。join()方法可以等子进程结束后再继续往下执行,通常用于进程间的同步。
from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('Child process will start.')
    p.start()
    p.join()
    print('Child process end.')
1.2、Pool
  • 如果要启动大量的子进程,可以用进程池的方式批量创建子进程。
  • 对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须调用close(),调用close()之后就不能继续添加新的Process了。
1.3、进程间通信
  • Process之间需要通信的,python的multiprocessing模块提供了Queue、Pipes等多种方式来交换数据。
2、多线程
  • Python的多线程是真正的Posix Thread,而不是模拟出来的线程。
  • Python的标准库提供了两个模块:_thread和threading。多数情况下,只需要使用threading这个高级模块。
  • 启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()并开始执行。\
  • 任何进程都会默认启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,永远返回当前线程的实例。主线程的名字叫MainThread
2.1、Lock
  • 可以给函数上锁,当某个线程执行该函数时,可以说线程因为获得了锁,因此其他线程不能同时执行该方法,只能等待,直到锁被释放后,获得该锁以后才能修改。可以通过threading.Lock()来实现。
  • lock.acquire()获取锁,然后继续执行代码。在try…finally来确保锁一定会被释放lock.release()。
2.2、多核cpu



3、ThreadLocal
  • 全局变量是一个ThreadLocal对象(threading.local()),每个Thread都可以对它读写某个属性,但互不影响。可以理解为每个ThreadLocal是一个dict,可以绑定任意变量。
  • threading.Thread(target=创建线程方法名, args=(‘参数名’, ), name=线程名)
  • 最常用的地方是为每个线程绑定一个数据库连接、http请求、用户身份信息等。
  • 一个ThreadLocal变量虽然是全局变量,但是每个线程都只能读写自己线程的独立副本。
4、线程vs进程
  • 操作系统在切换进程或者线程时需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后把新的环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。
4.1、计算密集型 vs IO密集型
5、分布式进程
  • 在Thread和Process中,应当优先选Process,因为其更稳定。Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个cpu上。