一,前言
-
进程:是程序,资源集合,进程控制块组成,是最小的资源单位
- 特点:就对Python而言,可以实现真正的并行效果
- 缺点:进程切换很容易消耗cpu资源,进程之间的通信相对线程来说比较麻烦
-
线程:是进程中最小的执行单位。
- 特点无法利用多核,无法实现真正意义上是并行效果。
- 优点:对于IO密集型的操作可以很好利用IO阻塞的时间
二,GIL(全局解释器锁)
python目前有很多解释器,目前使用最广泛的是CPython,还有PYPY和JPython等解释器,但是使用最广泛的还是CPython解释器,而对于全局解释器锁来说,就是在CPython上面才有的,它的原理是在解释器层面加上一把大锁,保证同一时刻只能有一个python线程在解释器中执行。
对于计算密集型的python多线程来说,无法利用到多线程带来的效果, 在2.7时计算密集型的python多线程执行效率比顺序执行的效率还低的多,在python3.5中对这种情况进行了优化,基本能实现这种多线程执行时间和顺序执行时间差不多的效果。
对于I/O密集型的python多线程来说,GIL的影响不是很大,因为I/O密集型的python多线程进程,每个线程在等待I/O的时候,将会释放GIL资源,供别的线程来抢占。所以对于I/O密集型的python多线程进程来说,还是能比顺序执行的效率要高的。
python的GIL这个东西。。。比较恶心,但是由于CPython解释器中的很多东西都是依赖这个东西开发的,如果改的话,将是一件浩大的工程。。。所以到现在还是存在这个问题,这也是python最为别人所诟病的地方。。。
三,多线程
多线程相当于一个并发(concunrrency)系统。并发系统一般同时执行多个任务。如果多个任务可以共享资源,特别是同时
写入某个变量的时候,就需要解决同步的问题,比如多线程火车售票系统:两个指令,一个指令检查票是否卖完,另一个指令,多
个窗口同时卖票,可能出现卖出不存在的票。
3.1 python实现多线程
python实现多线程有两种方式,分别是直接调用和继承调用,如下实例:
直接调用:
import threading import time # 定义线程运行函数 def mv(): print('播放=========') time.sleep(2) print('ending=======') # 带参数方式 def play(name): print("打游戏======"+name) time.sleep(3) print("ending======") if __name__ == '__main__': th = threading.Thread(target=mv) th2 = threading.Thread(target=play, args=("LOL",)) th.start() th2.start()
继承调用:
import threading import time class MyThread(threading.Thread): def __init__(self, num): super(MyThread, self).__init__() self.num = num def run(self): print("play game %s======" % self.num) time.sleep(2) print("end play") if __name__ == '__main__': mt = MyThread(1) mt2 = MyThread(2) mt.start() mt2.start()
可以看到直接调用是导入threading模块并定义一个函数,之后实例化threading.Thread类的时候,将刚定义的函数名通过target参数传递进去,然后调用实例的start()方法启动一个线程。
而继承式调用是创建一个类继承自threading.Thread类,并在构造方法中调用父类的构造方法,之后重写run方法,run方法中就是每个线程起来之后执行的内容,就类似于前面通过target参数传递进去的函数。之后以这个继承的类来创建对象,并执行对象的start()方法启动一个线程。
从这里可以看出,其实。。