引入

什么是线程、进程与线程的关系

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
'''
🔰证明多线程是共享同一个进程内存空间数据的