1、线程与进程
进程:一个程序的执行实例就是一个进程,每一个进程提供执行程序所需的所有资 源。(进程本质上是资源的集合),操作系统管理在其上面运行的所有进程,并为这些进程公平的分配时间。
线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
2、单线程
from time import ctime,sleep
def music(func):
for i in range(2):
print ("I was listening to %s. %s" %(func,ctime()))
sleep(1)
def movie(func):
for i in range(2):
print ("I was at the %s! %s" %(func,ctime()))
sleep(5)
if __name__ == '__main__':
music(u'爱情买卖')
movie(u'阿凡达')
print ("all over %s" %ctime())
3、多线程
Python
的标准库提供了两个模块:_thread
和threading
,_thread
是低级模块,它不支持守护线程,当主线程退出时,所有子线程都会被强行退出。threading
是高级模块,对_thread
进行了封装,支持守护线程,绝大多数情况下,我们只需要使用threading
这个高级模块。
import threading
from time import ctime,sleep
def music(func,loop):
for i in range(loop):
print ("I was listening to %s. %s" %(func,ctime()))
sleep(1)
def movie(func,loop):
for i in range(loop):
print ("I was at the %s! %s" %(func,ctime()))
sleep(5)
#创建线程组
threads = []
#创建线程t1,添加到线程组,目标函数,以及传参
t1 = threading.Thread(target=music,args=('爱情买卖',2))
threads.append(t1)
#创建线程t1,添加到线程组
t2 = threading.Thread(target=movie,args=('阿凡达',3))
threads.append(t2)
if __name__ == '__main__':
#启动线程
for t in threads:
t.start()
#守护线程
for t in threads:
# join()的作用是,等待每个线程终止。
t.join()
print ("all over %s" %ctime())
3、多进程
多进程multiprocessing模块提供远程与本地的并发,在一个multiprocessing库的典型使用场景下,所有的子进程都是由一个父进程启动起来的,这个父进程成为madter进程,这个父进程非常重要,他会管理一系列的对象状态,一旦这个进程退出,子进程很可能处于一个不稳定的状态,这个进程最好尽可能做最少的事情,以便保持其稳定性。
Process([group [, target [, name [, args [, kwargs]]]]])
- group分组,实际上不使用
- target表示调用对象,你可以传入方法的名字
- args表示给调用对象以元组的形式提供参数,比如target是函数a,他有两个参数m,n,那么该参数为args=(m, n)即可
- kwargs表示调用对象的字典
- name是别名,相当于给这个进程取一个名字
import multiprocessing
from time import ctime,sleep
def music(func,loop):
for i in range(loop):
print ("I was listening to %s. %s" %(func,ctime()))
sleep(1)
def movie(func,loop):
for i in range(loop):
print ("I was at the %s! %s" %(func,ctime()))
sleep(5)
#创建进程组
threads = []
#创建进程t1,添加到进程组,目标函数,以及传参
t1 = multiprocessing.Process(target=music,args=('爱情买卖',2))
threads.append(t1)
#创建线程t1,添加到进程组
t2 = multiprocessing.Process(target=movie,args=('阿凡达',3))
threads.append(t2)
if __name__ == '__main__':
#启动进程
for t in threads:
t.start()
#守护进程
for t in threads:
t.join()
print ("all over %s" %ctime())
如果要启动大量的子进程,可以用进程池的方式批量创建子进程
python的用多进程池来管理所有的进程,步骤如下:
1)实例化进程池 p=Pool()
。
2)通过apply_async()
函数向进程池中添加进程。
3)通过p.close()
函数关闭进程池,关闭之后无法继续添加新的进程。与此同时,CPU开始从进程池中取进程,若CPU有N核,则CPU每次只会取N个进程,待N个进程全部执行完毕,再取出N个。直到将进程池中的进程取完为止。
4)通过p.join()
函数等待所有进程被取出并执行完毕。
# 如果有大量进程,则可以用进程池,批量添加
from datetime import datetime
from multiprocessing import Process,Pool
import os,time
def music(func,loop):
for i in range(loop):
print ("I was listening to %s. %s" %(func,time.ctime()))
time.sleep(1)
def movie(func,loop):
for i in range(loop):
print ("I was at the %s! %s" %(func,time.ctime()))
time.sleep(5)
if __name__ =='__main__': #执行主进程
# 主进程
print('这是主进程,进程编号:%d' % os.getpid())
t_start = datetime.now()
pool = Pool()
for i in range(8): # CPU有几核,每次就取出几个进程
pool.apply_async(music, args=('爱情买卖',2))
pool.apply_async(music, args=('阿凡达',3))
pool.close() # 调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了
pool.join() # 对Pool对象调用join()方法会等待所有子进程执行完毕
t_end = datetime.now()
print('主进程用时:%d毫秒' % (t_end - t_start).microseconds)
4、进程间通信
multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
Pipe可以是单项(half-duplex),也可以是双向(duplex)。通过multiprocessing.Pipe(duplex=False)创建单向管道(默认双向)。一个进程从Pipe一端输入对象,然后被Pipe另一端的进程接收,单向管道只允许管道的一端的进程输入,而双向管道从两端输入。
import multiprocessing
def proc1(pipe):
pipe.send('hello')
print('procl rec:',pipe.recv())
def proc2(pipe):
print('proc2 rec:', pipe.recv())
pipe.send('hello too')
if __name__=='__main__':
multiprocessing.freeze_support()
pipe = multiprocessing.Pipe()
p1 = multiprocessing.Process(target=proc1,args=(pipe[0],))
p2 = multiprocessing.Process(target=proc2,args=(pipe[1],))
p1.start()
p2.start()
p1.join()
p2.join()
Queue类与Pipe相似,都是先进先出的结构,但是 Queue类允许多个进程放入,多个进程从队列取出对象。Queue类使用Queue(maxsize)创建,maxsize表示队列中可以存放对象的最大数量。
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('Put %s to queue...' % value)
q.put(value)
time.sleep(random.random())
# 读数据进程执行的代码:
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__':
# 父进程创建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()