随着计算机硬件水平的发展,CPU也从单核单任务变成可以支持多核多任务。例如intel的四核四线程,四核八线程。可以同时支持多个任务。最常见的就是你可以一边听着歌与此同时还能使用电脑进行其他的操作。
进程与线程的区别
进程是操作系统中应用程序的执行实例,而线程是进程内部的一个执行单元。这句话怎么理解呢?比如说你打开网易云音乐,此时就创建了一个进程来执行网易云音乐软件的相关操作。而你在听歌的同时,还能下载,此时执行网易云音乐的进程里面就包含两个线程,一个线程用于播放,另一个用于下载。所以进程于线程的关系就是:一个进程可以包含多个线程,多个线程之间可以并行执行,但是一个线程只属于一个进程。并且操作系统会为每一个进程单独分配资源(主要指内存),而属于同一进程的不同线程之间资源是共享的,不会单独分配。这就带来了资源分配的问题。
多线程编程的优点
多线程的运行有如下优点:
- 多线程可以将占据时间长的任务放到后台处理。例如下载任务,系统对于主程序有个响应时间限制。如果将下载等耗时唱的任务放在主线程,就会造成假死现象。
- 能够增加程序的运行速度。例如在频繁进行IO操作,并且数据量还比较大时,可以将一个数据用于读取数据,另一个线程对于线程进行相关操作。
Python多线程编程
全局解释器GIL
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设置之初就考虑到要在主循环中,同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在CPU中运行。同样,虽然Python解释器可以“运行”多个线程,但任意时刻,只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(global interpreter lock,GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。在多线程环境中,Python虚拟机按一下方式执行:
(1) 设置GIL
(2) 切换到一个线程去运行
(3) 运行:
a. 指定数量的字节码的指令,或者
b. 线程主动让出控制(可以调用time.sleep(0))
(4) 把线程设置为睡眠状态
(5) 解锁GIL
(6) 再次重复以上所有步骤
上述过程如下图所示:
python多线程适用范围
在python中,多线程的最好的处理的问题是对I/O密集型的操作,也就是说,在计算密集型的操作的时候,是不适合使用多线程的,因为在同一时刻,还是只有一个线程使用到cpu,但是I/O是阻塞的方式,从而python的多线程还是用在I/O操作上面,例如有大量用户的输入等IO操作。
python模块介绍
Python的标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。下面介绍threading
thread和threading模块
threading模块提供的方法
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的- 线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start():启动线程活动。
- join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
多线程的实现
使用threading创建线程
通过继承子类创建多线程
在Python中可以通过继承threading类来创建线程。通过继承threading模块中的Thread类来创建新类。在新类中重载run方法,然后就可以通过start创建线程。下面是使用代码创建线程。
import threading
class MyThread(threading.Thread):
def __init__(self,num):
threading.Thread.__init__(self)
self.num=num
def run(self): #重载run方法
print("I am ",self.num)
t1=MyThread(1)
t2=MyThread(2)
t3=MyThread(3)
t1.start()
t2.start()
t3.start()
通过函数创建
通过函数创建利用的是threading.Thread(),将函数作为参数传入,其实也相当于重载了线程的run方法。
import threading
import time
def run(x,y):
for i in range(x,y):
print("threading %s is running"%threading.current_thread().getName())
print(i)
print("%s is finished"%threading.current_thread().getName())
def func():
time.sleep(2)
for i in range(6):
print("threading %s is running"%threading.current_thread().getName())
print(i)
print("%s is finished"%threading.current_thread().getName())
t1=threading.Thread(target=run,args=(12,20),name="t1")
t1.start()
t2=threading.Thread(target=run,args=(7,11),name="t2")
t2.start()
t3=threading.Thread(target=func,name="func")
t3.start()
在这里对于python对线程基本操作有了一个了解。下篇介绍多线程同步问题以及如何使用python实现生产者消费者模型。
参考资料
[1].《Python宝典》,杨佩璐、宋强著
[2].python多线程编程
[3].用python多线程实现生产者消费者模式
[4].专题八.多线程编程之thread和threading