352-线程

多任务的实现方法有:

掌握:

线程的介绍

使用多任务的另一种方式

进程是分配资源的最小单位,一旦创建一个进程就会分配一定的资源,就像跟两个人聊QQ就需要打开两个QQ软件一样是比较浪费资源的.
线程是程序执行的最小单位,实际上进程只负责分配资源,而利用这些资源执行程序的是线程,也就说进程是线程的容器,一个进程中最少有一个线程来负责执行程序.同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源.这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)跟两个人聊天一样,实现多任务的同时也节省了资源

为什么使用多线程?

进程是分配资源的最小单位,
线程是执行任务的最小单位。

进程和线程是包含的关系:

什么时候使用线程?

区别

多进程:在CPU允许的情况下是并行,否则是并发。
多线程:多任务,底层是并发,单个核心里执行

多线程的作用

一个python程序,在执行的时候有一个主进程,进程默认有一个线程用来执行程序,这个线程成为主线程

352-线程_image.png

在进程创建一个新的线程,这个线程成为子线程

352-线程_image-1.png

多线程编程

总结就是三步走:

  1. 导入threading模块:import threading
  2. 创建线程对象:线程对象 = threading.Thread(target=函数名, args=(参数1, 参数2, ...))
  3. 启动线程执行任务:线程对象.start()

通过线程类创建线程对象

线程对象 = threading.Thread(target=函数名, args=(参数1, 参数2, ...))

参数:

示例代码(单线程):

import time

def music():
    for i in range(2):
        print("正在听音乐")
        time.sleep(1)

def game():
    for i in range(2):
        print("正在玩游戏")
        time.sleep(1)

if __name__ == "__main__":
    music()
    game()

示例代码(多线程):

import time
# 第一步:导入多线程模块
import threading

def music():
    for i in range(2):
        print("正在听音乐")
        time.sleep(1)

def game():
    for i in range(2):
        print("正在玩游戏")
        time.sleep(1)

if __name__ == "__main__":
    # 第二步:创建线程对象
    music_thread = threading.Thread(target=music)
    game_thread = threading.Thread(target=game)
    # 第三步:启动线程执行任务
    music_thread.start()
    game_thread.start()

示例代码(多线程+传参):

import time
# 第一步:导入多线程模块
import threading

def music(name):
    for i in range(2):
        print(f"{name}正在听音乐")
        time.sleep(1)

if __name__ == "__main__":
    # 第二步:创建线程对象,这里传入参数(‼️注意元组格式)
    music_thread = threading.Thread(target=music, args=("张三",))
    # 第三步:启动线程执行任务
    music_thread.start()

问题:多线程全局变量共享

多进程无法共享全局变量,
多线程可以共享全局变量,因为所有线程都共享同一个进程的资源。

import threading
import time

my_list = []

# 定义一个write_data()任务
def write_data():
    for i in range(5):
        my_list.append(i)
        print(my_list)
    print(my_list)

# 定义一个read_data()任务
def read_data():
    print(my_list)

if __name__ == "__main__":
    # 创建线程对象
    write_thread = threading.Thread(target=write_data)
    read_thread = threading.Thread(target=read_data)
    # 启动线程执行任务
    write_thread.start()
    time.sleep(1)
    read_thread.start()

问题:主线程和子线程的结束顺序

主线程会等待所有子线程执行完毕后才结束,所以主线程会阻塞等待子线程执行完毕。

示例代码:

import threading
import time

def task():
    for i in range(5):
        print(f"子线程{i}")
        time.sleep(1)

if __name__ == "__main__":
    # 创建线程对象
    thread = threading.Thread(target=task)
    # 启动线程执行任务
    thread.start()

    print("主线程结束(不等待子进程结束版本)")  # 这句话会提前打印,但是代码不会结束,因为主线程会等待子线程执行完毕后才结束
    # 如果想要等待子线程执行完毕后再执行print,可以使用join()方法
    thread.join()
    print("主线程结束(等待子进程结束版本)")

守护线程

守护线程:当主线程结束时,子线程也被结束
为什么叫守护主线程?

方案1:
sub_thread = threading.Thread(target=task, daemon=True)

方案2:
sub_thread = threading.Thread(target=task)
sub_thread.setDaemon(True)

示例代码:

import threading
import time

def task():
    for i in range(5):
        print(f"子线程{i}")
        time.sleep(1)

if __name__ == "__main__":
    sub_thread = threading.Thread(target=task, daemon=True)
    sub_thread.start()
    time.sleep(2)
    print("主线程结束")

问题:线程的执行顺序

线程之间的执行是无序的,谁先执行谁后执行,取决于CPU的调度算法。

单任务是一步一步执行
多任务是先将任务提交到cpu,再由cpu统一调度执行

![note] 新方法:获取进程的信息
通过current_thread()方法获取线程对象
current_thread = threading.current_thread()
通过`current_thread对象可以知道线程的相关信息,例如被创建的顺序
print(current_thread)

代码示例:

import threading
import time

def get_info():
    time.sleep(0.2)
    current_thread = threading.current_thread()
    print(current_thread)

if __name__ == "__main__":
    for i in range(5):
        sub_thread = threading.Thread(target=get_info)
        sub_thread.start()

线程和进程的对比在:350-多任务