python – 多线程就这样(Python – multithreading, that’s it)

前言:

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

什么是进程?

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

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

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

在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了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。

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

————————

preface:

Before talking about threads, pull the process.

What is a process?

The program can not run alone. Only when the program is loaded into memory, the system can allocate resources for it to run, and this executed program is called a process.

< strong > the difference between a program and a process is: < / strong >

  • Program: it is a collection of instructions, which is the static description text of process operation;
  • Process: it is an execution activity of a program and belongs to a dynamic concept.

In multiprogramming, we allow multiple programs to be loaded into memory at the same time, which can be executed concurrently under the scheduling of the operating system. This is such a design, which greatly improves the utilization of CPU. The emergence of process makes each user feel that they have their own CPU. Therefore, the process is proposed to realize multi-channel programming on the CPU.

Why thread when there is a process?

Process has many advantages. It provides multi-channel programming, which makes us feel that each of us has our own CPU and other resources, which can improve the utilization of the computer.

However, the process is flawed:

The process can only do one thing at a time. If you want to do two or more things at the same time, the process can’t do anything.

If a process is blocked during execution, such as waiting for input, the whole process will hang. Even if some work in the process does not depend on the input data, it will not be executed.

An operating system is compared to a factory. Each workshop is equivalent to a process. A workshop is composed of multiple employees who can produce in parallel. At this time, the efficiency of the workshop will rise. Each worker is a thread.

What is a thread?

Thread is the smallest unit that the operating system can schedule operations.

It is included in the process and is the actual operation unit in the process. A thread refers to a single sequential control flow in a process. Multiple threads can be concurrent in a process, and each thread executes different tasks in parallel.

< strong > difference between process and thread: < / strong >

  • Process is an executing program, which is the basic unit of resource allocation, and thread is the basic unit of CPU scheduling.
  • Processes are independent of each other, and resources cannot be shared between processes. A process has at least one thread, and each thread of the same process shares the resources (register, stack, context) of the whole process.
  • Thread creation and switching costs are less than processes.

Single thread

Let’s look at the single threaded mode first

# 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()))

Define two functions, one for eating and one for drinking.

Look at the results first:

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

It starts at 15:45:28 and ends at 15:45:36, taking a total of 8 s
Now the program is a single thread mode, eat two pig feet rice, dry two Maotai, and execute in sequence.

Multithreading

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

Next, modify the following code to realize watching movies while eating pig’s feet.

# 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) declares a thread as a daemon thread, which must be set before the start () method call. If it is not set as a daemon thread, the program will be suspended indefinitely.

After the child thread starts, the parent thread also continues to execute. When the parent thread finishes executing the last statement
After print (“all over {}”. Format (ctime())), you exit without waiting for the child thread, and the child thread ends at the same time.

Operation results of the above code:

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

According to the results, the child thread (diner and movie) and the main thread print (“all over {}”. Format (ctime())) are started at the same time.

After setting the daemon thread, when the main thread ends, the child thread will also end immediately and will no longer execute.

The main thread waits for the child thread to end

In order to make the main thread end after the execution of the daemon thread (child thread), we can use the join method to make the main thread wait for the execution of the child thread.

for t in threads:
    t.join()

The position of the join () method is outside the for loop. After the two sub threads are finished, the main process can be executed.

# 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()))

Look at the results first:

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

The sub threads diner and movie start at the same time, starting at 17:56:59 until the main process is called at 17:57:05,
The total time is 6 s. It takes 8 s for single thread and 2 s for multi thread.

Therefore, it can be seen that multithreading will be faster.