进程
python实现多进程,可以用fork()系统调用,但是不推荐这个方法,因为这个方法Windows没有。
我们还可以用模块multiprocessing,其中的Process类可以用来表示一个进程对象,看一个例子:
from multiprocessing import Process
import os
def run_proc(name):
print('%s process started, id is %s' % (name,os.getpid()))
if __name__ == '__main__':
#创建一个子进程,传入一个执行函数和参数
parePro = Process(target = run_proc, args=('child1',))
print('Parent started...')
#子进程开始执行
parePro.start()
#调用此方法后,等待子进程运行结束后在继续执行
parePro.join()
print('child1 process ended...')
运行结果:
Parent started...
child1 process started, id is 5040
child1 process ended...
[Finished in 0.3s]
上面的例子中,通过Process来创建进程每次只能创建一个进程,还可以使用Pool()方法:
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())
#参数5代表最多创建5个进程,所以下面的for循环
#中创建5个进程后,就不在继续创建,而是在等待
#前几个进程运行完毕后再继续创建进程
p = Pool(5)
for i in range(10):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
#调用此方法后不再创建进程
p.close()
p.join()
print('All subprocesses done.')
再来看进程间进行通信的实现,是通过python提供的Queue()模块实现的,一个进程往队列里写数据,另一个读数据:
from multiprocessing import Process,Queue
import os,time,random
def write(q):
print('Process to write: %s' % os.getpid())
for value in ['A','B','C']:
print('%s to queue...' % value)
q.put(value)
time.sleep(random.random())
time.sleep(3)
def read(q):
print('Process to read:%s' % os.getpid())
while True:
value = q.get(True)
print('get %s from queue' % value)
if __name__ == '__main__':
q = Queue()
childPro_Write = Process(target=write, args=(q,))
childPro_read = Process(target=read, args=(q,))
childPro_Write.start()
childPro_read.start()
childPro_Write.join()
#读数据是个死循环,所以调用此方法
childPro_read.terminate()
print('end...')
注意:上述两个例子的代码我在windows系统中执行感觉有点问题,最后结果不太对,但是放在linux(ubuntu16.04)中执行没有问题,具体原因我也不太清楚。
线程
线程的使用方法与进程类似,通过调用Threading模块实现:
import time, threading
#创建一个线程的例子
def loop():
print('thread %s runs...' % threading.current_thread().name)
n = 0
while n < 5:
n += 1
print('threading %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended' % threading.current_thread().name)
print('thread %s runs...' % threading.current_thread().name)
#创建线程的时候通过参数name来为线程定义一个名字
t = threading.Thread(target=loop, name='childThread')
t.start()
t.join()
print('thread %s ended' % threading.current_thread().name)
运行结果:
thread MainThread runs...
thread childThread runs...
threading childThread >>> 1
threading childThread >>> 2
threading childThread >>> 3
threading childThread >>> 4
threading childThread >>> 5
thread childThread ended
thread MainThread ended
[Finished in 5.1s]
当线程修改全局变量时,如果不添加锁的操作,可能会使结果出现问题,锁操作的用法:
首先在主线程处定义lock = threading.Lock()
在线程的运行函数中对全局变量的部分改为:
# lock.acquire()
# 锁操作,相当于声明此刻只有我这个线程能进行修改全局变量的操作,其他线程必须等待
# try:
# #修改全局变量的操作
# finally:
# lock.release()
# 声明其他线程能进行全局变量的修改操作了
锁操作能保证代码的正确执行,但是锁操作坏处也有很多,阻止了多线程并发执行,效率大大下降,还很有可能造成死锁。
在多线程中,每个线程都会从主线程得到一些数据作为自己的局部变量,不受其他线程干扰进行独立处理,在主线程函数调用通过参数传递给子线程这些数据时会有一些麻烦,另外最笨的方法是建一个全局字典,每个子线程有对应key和value值;还有一个办法就是使用ThreadLocal
import threading
local_heros = threading.local()
def process_heros():
hero_name = local_heros.hero_name
print('Hello, %s! (in %s)' % (hero_name, threading.current_thread().name))
def process_thread(name):
local_heros.hero_name = name
process_heros()
t1 = threading.Thread(target=process_thread, args=('阿狸',), name='thread-LoL_heros')
t2 = threading.Thread(target=process_thread, args=('路飞',), name='thread-OnePiece_heros')
t1.start()
t2.start()
t1.join()
t2.join()
#ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
进程与线程的区别
多进程的优点就是多进程稳定性高,一个子进程崩溃,不会影响其他进程的执行。缺点是创建进程开销大,并且同时运行的进程数也是有限的。
多线性比多进程稍微快一点,缺点出了上面说的锁操作外,还容易使进程崩溃,因为所有现存共享进程的内存,一个进程崩溃会使整个进程崩溃。