引入
什么是线程、进程与线程的关系
1.创建线程的开销远远小于创建进程
创建线程的开销要远远小于创建一个进程, 因为创建进程需要操作系统申请一块内存空间, 然后将数据从硬盘读到该进程的内存空间中, 并且一个进程至少要有一个线程, 而创建一个线程只是在进程的内存空间里创建, 无需申请空间, 几乎是发起系统调用的同时一个线程就启动起来了
2.进程之间、线程之间的关系
- 进程之间是竞争关系 : 不同的应用程序开启的不同进程共同争夺计算机硬件资源(cpu、网卡(网速)等)
- 线程之间是协作关系 : 同一个进程内的线程都是协作关系, 一个线程将数据处理交给另一个线程处理, 不会各自干个的
3.为何要用多线程
- 多线程必须是共用同一个进程的内存空间
- 线程比进程更轻量, 更容易创建和撤销, 创建几乎是线程的10-100倍, 大量线程需要动态和快速修改时,这一特性非常重要
- 如果程序是 I/O 密集型, 那么大量的线程彼此重叠运行, 将会提升程序运行的效率
- 在多核操作系统中, 为了最大限度的利用多核资源, 可以开启多个线程, 这比开进程的开销要小的多(Python中不适用:因为CPython的原因,存在GIL,同一时刻统一进程只有一个线程被执行,后面细说)
4.线程使用原理示例
我们使用的Pycharm编辑器, 一边敲代码, 一边编辑器自动帮你保存
- 场景一 : 我们使用多进程来做这件事, 进程与进程之间内存空间相互隔离, 如果我们要接收用户输入的代码, 然后将其保存到硬盘, 就需要申请内存空间来开设进程资源, 再创建一个管道, 将用户输入的数据放进去, 专门干保存数据的进程再从管道里拿数据
- 场景二 : 使用多线程来做这件事, 多线程之间共享同一个进程的内存空间, 数据的交互就变得十分方便, 并且线程创建所需资源远远小于进程
一.threading 模块介绍
前面我们所学的多进程管理模块multiprocessing, 它其实是完全模仿了threading模块的接口, 两者的使用非常相似, 下面一一展开介绍
二.开启线程的两种方式
1.方式一
-
current_thread().name
: 查看线程名字,默认 Thread-1
from threading import Thread,current_thread
import os
n = 100
def task(name):
global n
n = 22
print(f"子线程 : {name} {os.getpid()} {current_thread().name} {n}")
if __name__ == '__main__':
p = Thread(target=task,args=("派大星",))
p.start()
p.join()
print(f"主线程 : {os.getpid()} {current_thread().name} {n}")
'''输出
子线程 : 派大星 120 Thread-1 22
主线程 : 120 MainThread 22
'''
- 方式二 : 自定义类
from threading import Thread,current_thread
import os
n = 100
class Mythread(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self) -> None:
global n
n = 22
print(f"子线程 : {self.name} {os.getpid()} {current_thread().name} {n}")
if __name__ == '__main__':
p = Mythread("海绵宝宝")
p.start()
p.join()
print(f"主线程 : {os.getpid()} {current_thread().name} {n}")
'''输出
子线程 : 海绵宝宝 14664 海绵宝宝 22
主线程 : 14664 MainThread 22
'''
三.对比一个进程下开多个进程和多个线程的区别
1.开多个进程与线程时间上的对比
🍑多进程
from multiprocessing import Process
import os,time
def task():
print("多进程")
if __name__ == '__main__':
pc_li = []
start_time = time.time()
for i in range(10):
p = Process(target=task)
p.start()
pc_li.append(p)
for i in pc_li:
i.join()
print("用时:%s"%(time.time()-start_time)) # 用时:1.005824089050293
print(f"{os.getpid()}end")
🍑多线程
from threading import Thread
import os,time
def task():
print("多线程")
if __name__ == '__main__':
pc_li = []
start_time = time.time()
for i in range(10):
p = Thread(target=task)
p.start()
pc_li.append(p)
for i in pc_li:
i.join()
print("用时:%s"%(time.time()-start_time)) # 用时:0.0019948482513427734
print(f"{os.getpid()}end")
🔰 10 个进程用时 1 秒多, 10 个线程用时 0.002 秒
2.查看多进程和多线程的PID
🍑多进程
from multiprocessing import Process
import os,time
def task():
print(f"子进程PID:{os.getpid()}")
if __name__ == '__main__':
pc_li = []
start_time = time.time()
for i in range(3):
p = Process(target=task)
p.start()
pc_li.append(p)
for i in pc_li:
i.join()
print(f"主进程PID:{os.getpid()}")
'''输出
子进程PID:14064
子进程PID:16424
子进程PID:9904
主进程PID:15184
'''
🍑多线程
from threading import Thread
import os,time
def task():
print(f"子线程PID:{os.getpid()}")
if __name__ == '__main__':
pc_li = []
start_time = time.time()
for i in range(3):
p = Thread(target=task)
p.start()
pc_li.append(p)
for i in pc_li:
i.join()
print(f"主线程PID:{os.getpid()}") # 主线程也可以说是主进程
'''输出
子线程PID:832
子线程PID:832
子线程PID:832
主线程PID:832
'''
🔰 主进程与其下开启的子进程"PID"都各不相同, 而主线程与其下开启的子线程"PID"都相同
3.验证同一进程下的多线程是否共享该进程的数据
from threading import Thread
import time
x = 22
def task():
global x
x = 100 # 将数据改成100
print(f"子线程1打印:{x}")
def task2():
time.sleep(0.1) # 保证进程2是在进程1之后取x
print(f"子线程2打印:{x}")
if __name__ == '__main__':
p = Thread(target=task)
p2 = Thread(target=task2)
p.start()
p2.start()
p.join()
p2.join()
print(f"主进程打印:{x}")
'''输出
子线程1打印:100
子线程2打印:100
主进程打印:100
'''
🔰证明多线程是共享同一个进程内存空间数据的