python 多线程就这样

前言:

讲线程之前,先扯一下进程。

什么是进程?

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。


程序和进程的区别就在于:

  • 程序:是指令的集合,它是进程运行的静态描述文本;
  • 进程:是程序的一次执行活动,属于动态概念。

在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独CPU,因此,进程就是为了在CPU上实现多道编程而提出的。


有进程为什么还要线程?

进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。

但是,进程也有缺陷:

进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

一个操作系统比喻成一个工厂,每个车间就相当于一个进程,一个车间有多个员工组成,这些员工都可以并行进行生产,这时候车间效率是不是就起来了,每个工人就是线程。

什么是线程?

线程是操作系统能够进行运算调度的最小单位。

它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

进程和线程的区别:

  • 进程是一段正在执行的程序,是资源分配的基本单元,而线程是CPU调度的基本单元。
  • 进程间相互独立进程,进程之间不能共享资源,一个进程至少有一个线程,同一进程的各线程共享整个进程的资源(寄存器、堆栈、上下文)。
  • 线程的创建和切换开销比进程小。


单线程

先来看看单线程的模式

# coding=utf-8
from time import ctime, sleep  # ctime 获取当前时间

def dinner(func):
    for i in range(2):
        print("I'm having a meal: {}, 时间: {}" .format(func, ctime()))
        sleep(1)  # 休眠 1 s

def drink(func):
    for i in range(2):
        print("I'm drinking: {}, 时间: {}" .format(func, ctime()))
        sleep(3)

if __name__ == '__main__':
    dinner("猪脚饭")
    drink("茅台")
    print("all over {}" .format(ctime()))

定义两个函数,一个吃饭,一个喝酒。

先看结果:

I'm having a meal: 猪脚饭, 时间: Sat Jan 15 15:45:28 2022
I'm having a meal: 猪脚饭, 时间: Sat Jan 15 15:45:29 2022
I'm drinking: 茅台, 时间: Sat Jan 15 15:45:30 2022
I'm drinking: 茅台, 时间: Sat Jan 15 15:45:33 2022
all over Sat Jan 15 15:45:36 2022

开始是 15:45:28,结束是 15:45:36 ,总耗时 8 s
现在的程序就是单线程模式,吃两口猪脚饭,干两口茅台,顺序执行。



多线程

python多线程官网介绍:
https://docs.python.org/zh-cn/3.7/library/threading.html

以下来修改以下代码,实现一边吃猪脚饭,一边看电影。

# coding=utf-8
import threading
from time import ctime, sleep  # ctime 获取当前时间

def dinner(func):
    for i in range(2):
        print("I'm having a meal: {}, 时间: {}" .format(func, ctime()))
        sleep(1)  # 休眠 1 s

def movie(func):
    for i in range(2):
        print("I'm watching movies: {}, 时间: {}" .format(func, ctime()))
        sleep(3)

threads = []  # 创建一个空数组
# 使用threading.Thread()方法,target:调用 dinner 方法,args:传参 
thread1 = threading.Thread(target=dinner, args=("猪脚饭",))
# 将创建好的线程 thread1 追加到 threads 中
threads.append(thread1)
print()  # 换行
thread2 = threading.Thread(target=movie, args=("阿甘正传",))
threads.append(thread2)

if __name__ == '__main__':
    # for 循环遍历 threads 数组
    for t in threads:
        t.setDaemon(True)
        # 开始线程活动
        t.start()
    print()
    print("all over {}" .format(ctime()))

setDaemon()

setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。

子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句
print("all over {}" .format(ctime())) 后,没有等待子线程,直接就退出了,同时子线程也一同结束。

以上代码运行结果:

I'm having a meal: 猪脚饭, 时间: Sat Jan 15 16:18:29 2022
I'm watching movies: 阿甘正传, 时间: Sat Jan 15 16:18:29 2022

all over Sat Jan 15 16:18:29 2022

查看结果,子线程(dinner 和 movie)和主线程 print("all over {}" .format(ctime())) 都是同一时间启动。

设置守护线程之后,当主线程结束时,子线程也将立即结束,不再执行。

主线程等待子线程结束

为了让守护线程(子线程)执行结束之后,主线程再结束,我们可以使用 join 方法,让主线程等待子线程执行。

for t in threads:
    t.join()

join()方法的位置是在for循环外,等两个子线程结束后,再执行主进程。

# coding=utf-8
import threading
from time import ctime, sleep  # ctime 获取当前时间

def dinner(func):
    for i in range(2):
        print("I'm having a meal: {}, 时间: {}" .format(func, ctime()))
        sleep(1)  # 休眠 1 s

def movie(func):
    for i in range(2):
        print("I'm watching movies: {}, 时间: {}" .format(func, ctime()))
        sleep(3)

threads = []
thread1 = threading.Thread(target=dinner, args=("猪脚饭",))
threads.append(thread1)
print()  # 换行
thread2 = threading.Thread(target=movie, args=("阿甘正传",))
threads.append(thread2)

if __name__ == '__main__':
    for t in threads:
        t.setDaemon(True)
        t.start()
    for t in threads:
        t.join()
    print()
    print("end:  {}" .format(ctime()))

先看结果:

I'm having a meal: 猪脚饭, 时间: Sat Jan 15 17:56:59 2022
I'm watching movies: 阿甘正传, 时间: Sat Jan 15 17:56:59 2022
I'm having a meal: 猪脚饭, 时间: Sat Jan 15 17:57:00 2022
I'm watching movies: 阿甘正传, 时间: Sat Jan 15 17:57:02 2022

end:  Sat Jan 15 17:57:05 2022

子线程 dinner 和 movie 是同时开始的,17:56:59 开始,直到调用主进程为17:57:05 ,
总耗时 6 s。单线程的时候耗时 8 s,使用多线程耗时减少 2 s。

所以可以看出多线程执行程序会快一些。

原文地址:https://www.cnblogs.com/wwho/p/15808528.html