Python 多任务编程 - 多线程


文章目录

  • Python 多任务编程 - 多线程
  • 1. 线程的介绍
  • 1.1 实现多任务的另一种方式
  • 1.2 为什么使用多线程?
  • 1.3 多线程的作用
  • 2. 多线程完成多任务
  • 2.1 线程的创建步骤
  • 2.2 通过线程类创建线程对象
  • 2.3 线程创建与启动代码
  • 3. 线程执行带有参数的任务
  • 3.1 线程执行带有参数的任务
  • 3.2 args 参数的使用
  • 3.3 Kwargs 参数的使用
  • 4. 主线程和子线程的结束顺序
  • 4.1 设置守护主线程
  • 5. 线程间的执行顺序
  • 5.1 线程之间执行是无序的
  • 5.2 获取当前的线程信息
  • 6. 进程和线程对比
  • 6.1 关系对比
  • 6.2 区别对比
  • 6.3 优缺点对比



1. 线程的介绍

1.1 实现多任务的另一种方式

  • 在python 中,想要实现多任务还可以使用所线程来完成。

1.2 为什么使用多线程?

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

1.3 多线程的作用

同样需求使用多线程完成

def func_a():
	print("任务A")
def func_b():
	print("任务B")

func_a()
func_b()

2. 多线程完成多任务

2.1 线程的创建步骤

  1. 导入线程模块
import threading
  1. 通过线程类创建线程对象
线程对象 = threading.Thread(target=任务名)
  1. 启动线程执行任务
线程对象.start()

2.2 通过线程类创建线程对象

线程对象 = threading.Tread(target=任务名)

参数名

说明

target

执行的目标任务名,这里指的是函数名(方法名)

name

线程名,一般不用设置

group

线程组,目前只能用None

2.3 线程创建与启动代码

# 创建子线程
sing_thread = threading.Thread(target=sing)
# 创建子线程
dance_thread = threading.Thread(target=dance)
# 启动线程
sing_thread.start()
dance_thread.start()
  • 示例
import time
import threading


def sing():
    for i in range(3):
        print("唱歌")
        time.sleep(1)


def dance():
    for i in range(3):
        print("跳舞")
        time.sleep(1)


if __name__ == '__main__':
    sing_thread = threading.Thread(target=sing)
    dance_thread = threading.Thread(target=dance)

    sing_thread.start()
    dance_thread.start()

3. 线程执行带有参数的任务

3.1 线程执行带有参数的任务

参数名

说明

args

以元组方式传参

Kwargs

以字典方式传参

3.2 args 参数的使用

  • target:线程执行的函数名
  • args: 表示以元组方式给函数传参
sing_thread = threading_Thread(target=sing, args=(3,))
sing_thread.start()

3.3 Kwargs 参数的使用

# target: 线程执行的函数名
# kwargs: 表示以字典的方式给函数传参
dance_thread = threading.Thread(target=dance, kwargs={"count": 3})
dance_thread.start()
  • 示例
import time
import threading


def sing(num, name):
    for i in range(num):
        print(name, ": 唱歌")
        time.sleep(1)


def dance(count):
    for i in range(count):
        print("跳舞")
        time.sleep(1)


if __name__ == '__main__':
    sing_thread = threading.Thread(target=sing, args=(3, "xiaoming"))
    dance_thread = threading.Thread(target=dance, kwargs={"count": 2})

    sing_thread.start()
    dance_thread.start()
  • 【注】:

元组方式传参:元组方式传参一定要和参数的 顺序保持一致

字典方式传参:字典方式传参字典中的 key 一定要和 参数名保持一致


4. 主线程和子线程的结束顺序

  • 对比进程

主线程会等待所有的子线程执行结束后主线程再结束。

  • 示例
import time
import threading


def work():
    for i in range(10):
        print("工作")
        time.sleep(0.2)


if __name__ == '__main__':
    sub_thread = threading.Thread(target=work)
    sub_thread.start()

    # 主线程等待 1s, 后结束
    time.sleep(1)
    print("主线程结束了……")

    # 主线程会等待所有的子线程执行结束后主线程再结束。

4.1 设置守护主线程

要想主线程不等待子线程执行完成可以设置守护主线程。

# 设置守护主线程方式1,daemon=True 守护主线程
work_thread = threading.Thread(target=work, daemon=True)
# 设置守护主线程方式2
work_thread.daemon = True
work_thread.start()
# 主线程延迟 1s
time.sleep(1)
print("over")
  • 示例
  • 方式1
import time
import threading


def work():
    for i in range(10):
        print("工作")
        time.sleep(0.2)


if __name__ == '__main__':
    # sub_thread = threading.Thread(target=work)
    # sub_thread.start()
    # 设置守护主线程方式1,daemon=True 守护主线程
    sub_thread = threading.Thread(target=work, daemon=True)
    sub_thread.start()

    # # 设置守护主线程方式2
    # sub_thread = threading.Thread(target=work)
    # sub_thread.daemon = True
    # sub_thread.start()

    # 主线程等待 1s, 后结束
    time.sleep(1)
    print("主线程结束了……")

    # 主线程会等待所有的子线程执行结束后主线程再结束。
  • 方式2
import time
import threading


def work():
    for i in range(10):
        print("工作")
        time.sleep(0.2)


if __name__ == '__main__':
    # sub_thread = threading.Thread(target=work)
    # sub_thread.start()
    # # 设置守护主线程方式1,daemon=True 守护主线程
    # sub_thread = threading.Thread(target=work, daemon=True)
    # sub_thread.start()

    # 设置守护主线程方式2
    sub_thread = threading.Thread(target=work)
    sub_thread.daemon = True
    sub_thread.start()

    # 主线程等待 1s, 后结束
    time.sleep(1)
    print("主线程结束了……")

    # 主线程会等待所有的子线程执行结束后主线程再结束。

注:’ 线程对象.setDaemon(True) ’ 此方式自 3.10 起已弃用,可以使用 ’ 线程对象.daemon = True ’ 设置。


5. 线程间的执行顺序

线程之间的执行是无序的,是由 CPU调度决定某个线程先执行的

5.1 线程之间执行是无序的

for i in range(5):
	sub_thread = threading.Thread(target=task)
	sub_thread.start()

5.2 获取当前的线程信息

# 通过 current_thread 方法获取线程对象
current_thread = threading.current_thread()
# 通过 current_thread 对象可以知道线程的相关信息,例如被创建的顺序
print(current_thread)
  • 实例
import time
import threading


def task():
    time.sleep(1)
    # 通过 current_thread 方法获取线程对象
    current_thread = threading.current_thread()
    # 通过 current_thread 对象可以知道线程的相关信息,例如被创建的顺序
    print(current_thread)


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

    # 结论:多线程之间执行是无序,由于 cpu 调度。

6. 进程和线程对比

6.1 关系对比

  1. 线程是依附在进程里面的,没有进程就没有线程。
  2. 一个进程默认提供一条线程,进程可以创建多个线程。

6.2 区别对比

  1. 创建进程的资源开销要比创建线程的资源开销要大。
  2. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位。
  3. 线程不能够独立执行,必须依存在进程中。

6.3 优缺点对比

  1. 进程优缺点:
  • 优点:可以用多核
  • 缺点:资源开销大
  1. 线程优缺点:
  • 优点:资源开销小
  • 缺点:不能使用多核