- 1 前言
- 1.1 并发与并行
- 1.2 进程、线程、协程
- 2 线程介绍
- 2.1 什么是线程
- 2.2 为什么使用多线程
- 2.3 进程与线程之间的区别
- 3 线程实现
- 3.1 普通创建方式
- 3.2 自定义线程类
- 3.3 守护线程
- 3.4 主线程等待子线程结束
- 4 GIL(Global Interpreter Lock)全局解释器锁
- 参考
本系列打算将 Python 中的线程、进程以及协程做一个全面的总结,本文目前是第一部分——多线程。在正式进入线程的讲解之前,我们先来熟悉一下相关的概念。
1.1 并发与并行- 并发:在操作系统中,某一时间段,几个程序在同一个CPU上运行,但在任意一个时间点上,只有一个程序在CPU上运行。
- 并行:当操作系统有多个CPU时,一个CPU处理A线程,另一个CPU处理B线程,两个线程互相不抢占CPU资源,可以同时进行,这种方式成为并行。
即使在单核CPU上,如果运行速度较快,即使程序是在并发的交替执行,但是给我们带来的感受却像是在并行运行一样
- 进程:进程是执行中的程序(QQ在没有运行的情况下是应用程序,不是进程,只有我们双击运行它的时候,它才是进程),是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
- 线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位,也被称为轻量级进程。
- 协程:
2 线程介绍 2.1 什么是线程
线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位,也被称为轻量级进程。
2.2 为什么使用多线程已经有了进程,为什么还需要线程?为了回答这个问题,我们需要再回顾一下进程的定义:进程是系统进行资源分配和调度的基本单位。系统为每个进程都分配了资源,使得进程之间不受资源的干扰,提高了计算机的运行效率。但是进程还是有缺陷的,主要有以下两点:
- 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
- 进程在执行的过程中如果阻塞,整个进程就会挂起。
相比之下,多线程编程具有以下优点:
- 同个进程下的线程共享内存,所以同进程下的多线程之间通信方便;
- 同个进程下的不同线程之间可以代价更小的切换,从而实现并发执行;
- 进程是CPU资源分配的基本单位,线程是独立运行和独立调度的基本单位(CPU上真正运行的是线程)。
- 进程拥有自己的资源空间,一个进程包含若干个线程,线程与CPU资源分配无关,多个线程共享同一进程内的资源。
- 线程的调度与切换比进程快很多。
- 多进程对于多CPU,多线程对应多核CPU。
3 线程实现 3.1 普通创建方式
import threading
import time
def run(n):
print("task", n)
time.sleep(1)
print(n + '-2s')
time.sleep(1)
print(n + '-1s')
time.sleep(1)
print(n + '-0s')
time.sleep(1)
if __name__ == '__main__':
t1 = threading.Thread(target=run, args=("t1",)) # 注意("t1",)是元组,一定要加逗号
t2 = threading.Thread(target=run, args=("t2",))
t1.start()
t2.start()
运行结果如下:
task t1 task t2 t1-2s t2-2s t1-1s t2-1s t1-0s t2-0s3.2 自定义线程类
import threading
import time
class MyThread(threading.Thread):
def __init__(self, n):
super(MyThread, self).__init__() # 重构run函数必须写
self.n = n
def run(self):
print("task", self.n)
time.sleep(1)
print(self.n + '-2s')
time.sleep(1)
print(self.n + '-1s')
time.sleep(1)
print(self.n + '-0s')
time.sleep(1)
if __name__ == '__main__':
t1 = MyThread("t1")
t2 = MyThread("t2")
t1.start()
t2.start()
运行结果如下:
task t1 task t2 t1-2s t2-2s t1-1s t2-1s t1-0s t2-0s3.3 守护线程
当其它非守护线程结束时,程序退出
在下面的代码中,我们使用 setDaemon(True) 将子线程设置为守护线程,所以当主线程结束时,子线程也会随之结束,进而整个程序退出。
import threading
import time
def run(n):
print("task" + n)
time.sleep(1)
print(n + '-2s')
time.sleep(1)
print(n + '-1s')
time.sleep(1)
print(n + '-0s')
time.sleep(1)
if __name__ == '__main__':
t1 = threading.Thread(target=run, args=("t1",)) # 注意("t1",)是元组,一定要加逗号
t2 = threading.Thread(target=run, args=("t2",))
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
print("Done")
代码运行结果:
taskt1 taskt2 Done3.4 主线程等待子线程结束
为了让守护线程执行结束之后,主线程再结束,我们可以使用join方法,让主线程等待子线程执行。
import threading
import time
def run(n):
print("task" + n)
time.sleep(1)
print(n + '-2s')
time.sleep(1)
print(n + '-1s')
time.sleep(1)
print(n + '-0s')
time.sleep(1)
if __name__ == '__main__':
t1 = threading.Thread(target=run, args=("t1",)) # 注意("t1",)是元组,一定要加逗号
t2 = threading.Thread(target=run, args=("t2",))
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
t1.join()
t2.join()
print("Done")
代码运行结果如下:
taskt1 taskt2 t1-2s t2-2s t1-1s t2-1s t2-0s t1-0s Done4 GIL(Global Interpreter Lock)全局解释器锁 参考
- python多线程详解
- 图解Python 【第八篇】:网络编程-进程、线程和协程



