进程与线程
单核的CPU在一个时间片中只能执行一个程序,各个程序之间抢夺CPU资源
进程:进程就是一个程序在数据集上的一次动态执行过程,进程是资源分配的基本单位。程序运行时,系统会创建一个进程,并为进程分配资源。
线程:复杂代码块的执行,是程序执行的最小单位,也是进程中一个执行路径,每个线程都有自己的任务代码
进程与线程的关系
- 线程不可独立存在,需要依赖进程
- 一个进程可以有多个线程,多个线程共享进程的资源;多个进程间资源是独立的(CPU切换一个线程花费的开销比切换进程小,同时创建一个线程的开销也比进程小)
- 每个进程有独立的资源,当一个进程崩溃时,对其他进程没有影响–进程的稳定性高;但任何一个线程挂掉可能会造成进程崩溃,因为多个线程共享一个进程资源
- 由于GIL锁的缘故,python 中线程实际上是并发运行(即便有多个cpu,线程会在其中一个cpu来回切换,只占用一个cpu资源),而进程才是真正的并行(同时执行多个任务,占用多个cpu资源)
- IO密集型使用多线程(在等待时切换),计算密集型使用多进程(充分利用多核CPU)
并行与并发
- 并行:是指两个或者多个事件在同一时刻发生
- python中的进程属于并行
- 当一个CPU执行一个进程时,另一个CPU可以执行另一个进程
- 两个进程互不抢占CPU资源,能充分利用计算机资源,效率最高
- 并发:并发是指两个或多个事件在同一时间间隔发生
- python中的线程属于并发。
- 不管计算机有多少个CPU,不管你开了多少个线程,同一时间多个任务会在其中一个CPU来回切换,只占用一个CPU,效率并不高;
1、进程
1.1、进程的状态
任务数(进程数)往往大于cpu的核数,即一定有一些任务正在执行,而另外一些任务在等待cpu进行执行,因此导致了有了不同的状态
- 就绪态:运行的条件符合,正在等在空闲cpu执行
- 执行态:cpu正在执行其功能
- 等待态(阻塞):等待某些条件满足,例如一个程序sleep了,此时就处于等待态(再比如一些需要花费时间来等待来自用户、文件、数据库、网络等的输入输出的任务)
- 死亡
1.2、创建进程
创建进程的类:Process([group [, target [, name [, args [, kwargs]]]]])
- target表示调用对象
- args表示调用对象的位置参数元组
- kwargs表示调用对象的字典
- name为别名
- group实质上不使用。
进程的方法:
- is_alive()
- join([timeout])
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步 - run()
- start()
Process以start()启动某个进程 - terminate()。
import multiprocessing
import time
def worker(interval):
n = 5
while n > 0:
print("The time is {0}".format(time.ctime()))
time.sleep(interval)
n -= 1
if __name__ == "__main__":
p = multiprocessing.Process(target = worker, args = (3,))
p.start()
print "p.pid:", p.pid
print "p.name:", p.name
print "p.is_alive:", p.is_alive()
1.3进程池
如果想要启动大量的子进程,可以用进程池的方式批量创建子进程
-
主进程或者主线程会等待子进程或者子线程执行完毕后结束
-
创建进程池后,主进程不会等待进程池中的代码执行完
-
可以使用进程池的.join()方法等待子进程执行完
- p = Pool() 创建进程池,默认创建4个,如果想要更多,加上参数即可:p = Pool(8)
- p.close() 调用close()方法后,就不能再创建进程了
- p.join() 会等待所有子进程执行完毕
from multiprocessing import Pool
if __name__=='__main__':
print 'Parent process %s.' % os.getpid()
p = Pool() #创建进程池,默认创建4个
for i in range(5):
p.apply_async(func_name, args=(i,))
print 'Waiting for all subprocesses done...'
p.close() # 调用close方法后,就不能再创建进程了
p.join() #会等待所有子进程执行完毕
print 'All subprocesses done.'
- 进程 0,1,2,3是立刻执行的
- task 4要等待前面某个task完成后才执行
- 进程池Pool的默认大小是4,最多同时执行4个进程,Pool的默认大小是CPU的核数
1.4进程间通讯
进程间的通讯可以通过socket、文件读写、Queue队列完成
队列:队列类似于一条管道,元素先进先出。队列都是在内存中操作:进程退出,队列清空(队列是一个阻塞的形态)
以下,创建两个子进程,一个往队列里写数据,一个从队列里读数据:
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()
2、线程
2.1、线程的实现方式
绝大多数情况下,我们只需要使用threading这个高级模块。
实现方式有两种:
(1)函数式
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行
import time, threading
# 新线程执行的代码:
def loop():
print('thread %s is running...')
print('thread %s is running...' )
t = threading.Thread(target=loop, name='LoopThread')
t.start() # 启动一个线程
t.join() # 等待所有线程执行完毕
print('thread %s ended')
2)类式
import threading,time
# myThread类继承了线程类threading.Thread,可以重写run方法
class myThread(threading.Thread):
def __init__(self, name):
super().__init__()