提前说一点:如果你是专注于Python开发,那么本系列知识点只是带你入个门再详细的开发点就要去看其他资料了,而如果你和作者一样只是操作其他技术的Python API那就足够了。
首先我要给大家说一下进程和线程有什么区别,进程包含着线程,我们都打开过Windows的任务管理器,我们在其中看到的各种任务,每一个任务,就是一个进程,在这个进程下,有着多个线程组成了这个进程,我在网上找了一个更贴切的解释
Python允许你的任务是多进程执行的,在使用多进程的时候你需要注意两点
1、进程之间不共享资源,因为他们相互之间是独立的
2、主进程的结束,要等到所有子进程全部结束,这一点大家或许有疑惑,其实我们在运行一个程序时,一开始直接运行的进程就是主进程,也就是说主进程有且只有一个,而子进程就是我们在主进程中书写代码的方式开启的其他进程
3、进程之间执行是无序的
那么问题来了,多进程的应用在哪里?其实进程在开发中只有一个作用,就是充分利用CPU资源执行多任务运行,提高执行效率,打个比方说,某一个程序有多个功能,但是我们在平常开发的时候,只能是多个功能挨个执行,这个时候使用多进程可以让这些功能一起执行,就比如我们的系统就是一个多进程的东西,所以多进程他是在所要执行的任务之间没有必要关联,任务之间相对独立的情况下使用的一种提升程序效率的开发手段
对于进程,我们有两种运行模式,并发
和并行
,现在很多时候说的多进程都是主并行副并发,比如现在的普遍的8核CPU,它其实相当于只有8个人干活,这8个人如果总任务数小于8那就是并行,多余8个那就是8个人在力所能及的范围内并发的工作
多进程在使用的时候,使用方式如下
import multiprocessingdef p():for i in range(5) :print(i)if __name__ == '__main__' :a = multiprocessing.Process(target=p)a.start()for i in range(5) :print(str(i) + '主进程')结果:
0主进程
1主进程
2主进程
3主进程
4主进程
0
1
2
3
4
在不同系统上使用python的多进程的时候,有一个巨大的坑!!!!!如果你的程序运行在了Windows上,那么你创建子进程的操作一定要和我一样写在python的主方法里面,因为Windows上运行的时候,由于系统比较死板,它不会如linux那样直接开始运行Python解析器,它必须要先创建一个依托于Python的进程启动解析器,才去执行你的Python代码,这就导致,你如果不在主方法里面写,它会导致代码执行错乱,把开始子进程当做普通代码加载,无限次递归调用创建子进程方法,最后导致子进程创建失败
其次介绍一下Process这个类,该类有5个参数
group:指定进程组,这个参数不要设置,让它默认None就行,这个是Python保留的一个参数,当前3.0版本,你设置其他参数就会报错
target:进程要执行的任务,值是一个函数名,必备参数
name:进程名称,可以不设置
args:如果进程执行的任务需要传参,则以元组的形式传递
kwargs:也是用来传参的,不过是以字典的形式
Process常用方法有三个
start() 启动进程
join() 等待子进程执行完毕
terminate() 无论子进程的任务是否执行完毕,立马结束子进程Process
你如果想获取执行代码的进程的编号,那么需要使用os模块
import multiprocessing
import osdef p():print('当前子进程id:',os.getpid())if __name__ == '__main__' :a = multiprocessing.Process(target=p)a.start()print('当前主进程id:',os.getpid())结果:
当前主进程id: 9892
当前子进程id: 10220
当然你如果想看一下,当前执行代码的是那个进程,它的名字,就使用如下的方法
import multiprocessing
import osdef p():print('当前子进程id:',os.getpid(),'进程名字为:',multiprocessing.current_process())if __name__ == '__main__' :a = multiprocessing.Process(target=p)a.start()print('当前主进程id:',os.getpid(),'进程名字为:',multiprocessing.current_process())结果:
当前主进程id: 12432 进程名字为: <_MainProcess name='MainProcess' parent=None started>
当前子进程id: 7092 进程名字为: <Process name='Process-1' parent=12432 started>
当然os模块,还可以杀死进程
import multiprocessing
import osdef p():os.kill(os.getpid(),9) #9代表执行力度,9表示立马杀死print('当前子进程id:',os.getpid(),'进程名字为:',multiprocessing.current_process())if __name__ == '__main__' :a = multiprocessing.Process(target=p)a.start()print('当前主进程id:',os.getpid(),'进程名字为:',multiprocessing.current_process())结果:
当前主进程id: 5508 进程名字为: <_MainProcess name='MainProcess' parent=None started>
发现结果中没有子进程了
带参数执行方法的时候也要注意书写规范,传递元组位数要和方法形参一一对应,字典也要用形参的名字,传参的时候允许元组和字典一起使用,但是字典中的参数相当于是元组中的一个补充
import multiprocessingdef p(name,age):print('我是',name)if __name__ == '__main__' :a = multiprocessing.Process(target=p,args=('Tom',),kwargs={"age":12})a.start()
下面说说线程,我们往往觉得线程和进程说的是一个东西,但其实他们不是一个东西,而之所以大家会觉得它们两个是一个东西,是因为一个程序的执行默认情况下由进程负责,CPU进行调度,默认情况下一个进程中只有一个线程,所有导致很多人认为它们是一个东西
不过值得高兴的时,线程的操作方式和进程没差别,只不过部分的名字不一样而已
import threadingdef p(name,age):print('我是',name,threading.current_thread())if __name__ == '__main__' :#启动两个线程并打印它的名称a = threading.Thread(target=p,kwargs={"name":"Tom","age":12})a.start()b = threading.Thread(target=p,kwargs={"name":"Tom","age":12})b.start()结果:
我是 Tom <Thread(Thread-1, started 14528)>
我是 Tom <Thread(Thread-2, started 14732)>
这里给大家贯彻几个概念,第一是不管是线程还是进程,他们的运行都是无序的,下面以线程为例写一个测速程序
import threading
import timedef p():time.sleep(0.2)print('我是',threading.current_thread())if __name__ == '__main__' :for i in range(10) :a = threading.Thread(target=p)a.start()结果:
我是 <Thread(Thread-10, started 8824)>
我是 <Thread(Thread-5, started 11848)>
我是 <Thread(Thread-6, started 3796)>
我是 <Thread(Thread-2, started 11240)>
我是 <Thread(Thread-3, started 7480)>
我是 <Thread(Thread-4, started 8912)>
我是 <Thread(Thread-1, started 7240)>
我是 <Thread(Thread-8, started 9240)>
我是 <Thread(Thread-9, started 11236)>
我是 <Thread(Thread-7, started 3612)>
第二个是,无论是线程还是进程,主的永远会等待子的结束任务,它才会结束
import threading
import timedef p():while True :print('-----')time.sleep(1)if __name__ == '__main__' :a = threading.Thread(target=p)a.start()print('over')结果:
-----
over
-----
-----
。。。。。。。。
第三点是,线程之间共享资源,但是进程不共享,如下例子可以说明这一点
import threading
import timeaa = []def p():for i in range(3) :time.sleep(1)aa.append(i)print('add:',i)def q():time.sleep(5)print(aa)if __name__ == '__main__' :a = threading.Thread(target=p)a.start()b = threading.Thread(target=q)b.start()结果:
add: 0
add: 1
add: 2
[0, 1, 2]
第四点,在线程共享资源的时候,有些时候,如果你直接共享,肯定会出现问题,比如下面这种情况
import threading
import timeaa = 0def p():for i in range(1000000000) :global aaaa += 1print(aa)def q():for i in range(100000000) :global aaaa += 1print(aa)if __name__ == '__main__' :a = threading.Thread(target=p)a.start()b = threading.Thread(target=q)b.start()结果:
122831590
1016427767
当然我上面这个例子是最简单的一个,总之直接共享,很大可能不是报错就是数据不对,那么这种情况下怎么办呢?请看下篇知识点