多进程
Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程
os.fork():创建子进程
import os
print('process is start...', os.getpid())
child = os.fork()
if child == 0:
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
print('I (%s) just created a child process (%s).' % (os.getpid(), child))
# 输出
process is start... 22300
I (22300) just created a child process (22301).
I am child process (22301) and my parent is 22300.
multiprocessing(多进程模块)
multiprocessing模块就是跨平台版本的多进程模块。multiprocessing模块提供了一个Process类来代表一个进程对象。
from multiprocessing import Process
# 启动一个子进程并等待其结束
# 子进程执行的代码
def child_do(name):
print('I am child process, my name is %s (%s)...' % (name, os.getpid()))
if __name__ == '__main__':
print('parent process %s.' % os.getpid())
child = Process(target=child_do, args=('test',))
print('child process will start.')
child.start() # start()方法启动
child.join() # join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步
print('child process end.')
如果要启动大量的子进程,可以用进程池的方式批量创建子进程
from multiprocessing import Pool
import os, time, random
def task(name):
print('running task %s(%s)' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('task %s run %0.2f seconds.' % (name, end - start))
if __name__ == '__main__':
print('process is start %s' % os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(task, args=(i,))
print('waiting for all subprocesses done...')
p.close() # 对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了
p.join()
print('all subprocesses done')
上述代码中的pool.apply_async()是apply()函数的变体,apply_async()是apply()的并行版本,apply()是apply_async()的阻塞版本,使用apply()主进程会被阻塞直到函数执行结束,所以说是阻塞版本。apply()既是Pool的方法,也是Python内置的函数,两者等价。
子进程
很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出
subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出
# puthon在代码中运行命令
print('$ nslookup www.python.org')
r = subprocess.call(['nslookup', 'www.python.org'])
print('Exit code:', r)
进程间通信
Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
# 写进程
def write(q):
print('Writing Process %s', os.getpid())
for value in ['A', 'B', 'C']:
print('put %s in queue' % value)
q.put(value) # 放入队列
time.sleep(random.random())
# 读进程
def read(q):
print('Reading Process %s', os.getpid())
while True:
value = q.get() # 从队列中拿
print('get %s in queue' % value)
if __name__ == '__main__':
# 父进程创建Queue,并传给各个子进程
q = Queue()
# 传给写进程
pw = Process(target=write, args=(q,)) # 执行的函数式write,参数传递的是这个队列
# 传给读进程
pr = Process(target=read, args=(q,))
# 启动写进程
pw.start()
# 启动读进程
pr.start()
# 等待写进程完成
pw.join()
# 读进程是死循环,无法等待他结束,只能强行终止
pr.terminate()
多线程
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行
创建一个线程
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 end', threading.current_thread().name)
print('thread %s is running' % threading.current_thread().name) # 由于任何进程默认就会启动一个线程,我们把该线程称为主线程
t = threading.Thread(target=loop, name='LoopThread') # 创建了一个叫LoopThread的线程,执行的函数式loop
t.start() # 启动线程
t.join() # 等待线程
print('thread %s end.' % threading.current_thread().name) # threading.current_thread()返回当前线程的实例
给线程加锁
balance = 0
lock = threading.Lock() # 创建一个锁叫lock
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(1000000):
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)
当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止
我们用try…finally来确保锁一定会被释放
ThreadLocal
全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理
# 创建全局ThreadLocal对象:
local_school = threading.local()
def process_student():
# 获取当前线程关联的student:
std = local_school.student
print('Hello, %s (in %s)' % (std, 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()