线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
一.多进程
1. multiprocessing模块时跨平台版本的多线程模块
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 'Process will start.'
p.start()
p.join()
print 'Process end.'
2. poo线程池
对pool对象调用join方法会等待所有子进程执行完毕,调用join之前必须先调用close
from multiprocessing import Pool
import os, time, random
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()
p.join()
print 'All subprocesses done.'
3. 进程间通信:
multiprocessing中使用Queue、Pipes等方式来交换数据。
from multiprocessing import Process, Queue
import os, time, random
# 写数据进程执行的代码:
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__':
# 父进程创建Queue,并传给各个子进程:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,读取:
pr.start()
# 等待pw结束:
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止:
pr.terminate()
二.多线程
1. 启动一个线程就是把一个函数传入并创建Thread实例,然后调用start开始执行:
import time, threading
# 新线程执行的代码:
def loop():
print 'thread %s is running...' % threading.current_thread().name
n = 0
while n < 5:
n = n + 1
print 'thread %s >>> %s' % (threading.current_thread().name, n)
time.sleep(1)
print 'thread %s ended.' % threading.current_thread().name
print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name
2. 给变量加锁,防止被其他线程篡改:
import time, threading
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
balance = 10000 # 假定这是你的银行存款:
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
lock.acquire() # 先要获取锁
try:
change_it(n) # 放心地改吧
finally:
lock.release() # 改完了一定要释放锁
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print balance
锁的好处就是确保某段关键代码只能由一个线程从头到尾完整地执行。
坏处就是降低了效率,同时有可能出现死锁。
三.ThreadLocal
import threading
# 创建全局ThreadLocal对象:
local_school = threading.local()
def process_student():
print 'Hello, %s (in %s)' % (local_school.student, threading.current_thread().name)
def process_thread(name):
# 绑定ThreadLocal的student:
local_school.student = name
process_student()
t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
可以把local_student看成一个dict, key为每个线程名
ThreadLocal可以用于传递数据库连接。
四.分布式进程
python的分布式进程适合需要把繁重任务分布到多台机器的环境下。