到目前为止,前面的章节文章都是单线程运行。就像一个人工作,必须一件事结束,再开始另一件事情,这就是所谓的单线程
多线程就是雇佣多个人,一起做同一件事或者将不同的事分给不同的人去做,可以大幅度的提交效率
先来看下多线程需要的库:Python内置的线程库threading
线程的运行,通过threading,初始化得到一个实例,然后给他一个可以执行的函数,让他执行就不用管了,就像你在电脑上把音乐软件打开,然后隐藏到后台,它还是会继续工作,然后你继续做你的事情是一样的意思。
能开一个线程,就能开多个线程.....
在代码中初始化一个线程,并给他函数让他执行,他跑到后台去执行了,然后代码可以继续往下工作,先上一段代码理解下:
import threading, time
def loop():
print("loop start run")
time.sleep(5)
print("loop exit")
t = threading.Thread(target=loop, name="LoopThread")
t.start()
print("代码运行结束")
解释一下代码:
- 首先导入两个库,一个是线程threading,另一个是time库【主要是sleep函数】
- 然后在代码中,定义了loop函数,执行操作有打印loop start run,接着睡眠5秒钟,接着打印loop exit
- 再是初始化一个线程,目标函数是loop,给线程赋一个名字LoopThread
- 启动线程,然后不管了,脚本代码打印代码运行结束之后退出
先来看结果图:
python多线程从结果图来看,整体运行了5秒,但是代码运行结束在loop exit的前面,也就是说线程的运行情况和当前代码执行顺序无关,它只管运行它的。
就像是我有个任务——执行loop函数,我嫌函数无聊不想执行,我就找一个人来把我这个任务拿去执行,然后我做我的事。
理论上两个时间是重合的,时间会剪短,但是这里是看不出来的,因为代码执行的太快,时间和5秒相比毫无意义。
接下来定义两个,来测试一下时间问题:
import threading, time
def loop():
print("loop start run")
time.sleep(5)
print("loop exit")
t1 = threading.Thread(target=loop, name="LoopThread-1")
t2 = threading.Thread(target=loop, name="LoopThread-2")
t1.start()
t2.start()
time.sleep(3)
print("代码运行结束")
这个示例是基于上一个示例的,现在创建两个线程,都在运行,理论上睡眠时间是5+5+3=13秒的,来看下具体执行情况:
thread多线程加时还是短短的5.1秒,远小于13秒。
简单说下为什么:t1睡眠5秒的时间和t2睡眠5秒是重合的,还有代码睡眠3秒是在5秒的时间范围内的,所以最后的运行时间也就是程序开始,到程序全部结束的时间。
明白了线程的运行,然后继续了解下线程的其他知识点
知识点一:父线程和子线程
父线程和子线程是相对的。就拿刚才的示例来讲,代码执行是单线程,代码中创建了t1和t2,对于代码的单线程来讲,t1和t2是它的子线程,因为他们是被代码的单线程创建出来的。
知识点二:守护线程
守护线程和普通线程基本没什么区别,但是有一点特殊,即守护线程还没执行完,主线程退出,守护线程会强制退出;但是主线程执行结束,普通线程没结束,主线程会等待普通线程结束再退出
上一个示例代码和运行结果图:
import threading, time
def loop():
print("loop start run")
time.sleep(5)
print("loop exit")
t = threading.Thread(target=loop, name="LoopThread")
t.setDaemon(True)
t.start()
print("代码运行结束")
Python守护线程
子线程还没结束,主线程已经结束了,强制退出,运行时间仅0.2秒。
对比本文第一个示例【非守护线程】,总执行时间有5.1秒呢
知识点三:线程和进程
线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。一个线程是一个execution context(执行上下文),即一个cpu执行时所需要的一串指令。
进程:一个程序的执行实例就是一个进程。每一个进程提供执行程序所需的所有资源。(进程本质上是资源的集合)
一个进程有一个虚拟的地址空间、可执行的代码、操作系统的接口、安全的上下文(记录启动该进程的用户和权限等等)、唯一的进程ID、环境变量、优先级类、最小和最大的工作空间(内存空间),还要有至少一个线程。
知识点四:Python的线程是鸡肋?
用C、C++来写死循环,直接可以把全部核心跑满,4核就跑到400%,8核就跑到800%,但是Python是不行的。
因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
Python3更多教程——传送门
网友评论