进程和线程
线程是最小的执行单元,而进程由至少一个线程组成。
多进程
Unix下多进程和系统原生调用很像,使用fork()
# coding=utf-8
import os
print 'Process (%s) start...' % os.getpid()
pid = os.fork() # win 下没有该调用
if pid == 0: # 子进程返回0
print 'I am child process (%s) and my parent is %s' % (os.getpid(), os.getppid())
else: # 父进程返回子进程的pid
'I (%s) just created a child process (%s).' % (os.getpid(), pid)
multiprocessing
提供了跨平台的多进程支持
# coding=utf-8
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',)) # 传入子进程要执行的函数和参数,有点像Linux上创建线程的方式
print 'Process will start.'
p.start()
p.join() # 等待子进程执行结束
print 'Process end.'
Pool
# coding=utf-8
import os
import random
import time
from multiprocessing import Pool
def long_time_task(name):
print 'Run task %s (%s)...' % (name, os.getpid())
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print 'Task %s runs %0.2f seconds.' % (name, (end - start))
if __name__ == '__main__':
print 'Parent process %s.' % os.getpid()
p = Pool()
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print 'Waiting for all subprocesses done...'
p.close() # 调用后不能再增加新的Process
p.join() # 必须先调用close().等待所有子进程执行完毕
print 'All subprocesses done.'
进程间通信
提供了Queue
、Pipes
等多种方式来交换数据
# coding=utf-8
import random
from multiprocessing import Process, Queue
import time
def write(q):
for value in ['A', 'B', 'C']:
print 'Put %s to queue...' % value
q.put(value) # 写入队列
time.sleep(random.random())
def read(q):
while True:
value = q.get(True) # 从队列读取,无数据时阻塞
print 'Get %s from queue.' % value
if __name__ == '__main__':
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
pw.start()
pr.start()
pw.join()
pr.terminate()
tips:
在Unix/Linux下,
multiprocessing
模块封装了fork()
调用,使我们不需要关注fork()
的细节。由于Windows没有fork
调用,因此,multiprocessing
需要“模拟”出fork
的效果,父进程所有Python对象都必须通过pickle序列化再传到子进程去,所有,如果multiprocessing
在Windows下调用失败了,要先考虑是不是pickle失败了。
多线程
- Python的线程是真正的Posix Thread,而不是模拟出来的线程
-
thread
是低级模块,threading
是高级模块,对thread
进行了封装。绝大多数情况下,只需要使用threading
- 启动一个线程就是把一个函数传入并创建
Thread
实例,然后调用start()
开始执行:
t = threading.Thread(target=loop, name='LoopThread')
t.start()
Lock
多线程中为了保证不同线程访问同一个变量的安全性,需要在访问变量的时候加锁
lock = threading.Lock() # 获取锁实例
lock.acquire() # 获取线程锁
lock.release() # 释放线程锁
ThreadLocal
多线程下每个线程都有自己的数据,为了时数据访问方便,一般使用全局变量,但是全局变量会被别的线程访问,还必须加锁比较麻烦,ThreadLocal
的实例可以定义为全局变量,每个线程对其属性的访问都是独立的,相当于于线程的全局变量,虽然不同线程访问同一个ThreadLocal
实例,但是互不冲突
ThreadLocal
最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
进程 vs. 线程
IO密集型使用多线程能明显提升性能,而计算密集型任务使用多线程可能反而降低性能。
线程过多操作系统会耗费大量资源来进行调度,可能反而降低效率
分布式进程
在Thread和Process中,应当优选Process,因为Process更稳定,而且,Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上。
Python的multiprocessing
模块不但支持多进程,其中managers
子模块还支持把多进程分布到多台机器上。一个服务进程可以作为调度者,将任务分布到其他多个进程中,依靠网络通信。
原理是Queue
的远程访问,服务端将计算的参数写入参数Queue,客户端将参数从Queue中读取出来然后将计算结果写入结果Queue,最后服务端将结果从Queue中读取出来完成分布式计算。