认识进程与线程(python)

  一段时间没有更新博客了,今天和大家讲讲关于 python 进程和线程的知识点。(个人心得,多多指教!)

阶段一:并发与并行的深入理解

​ 并行一定是并发,但并发不一定是并行。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

​ 并行是相对的,并行是绝对的。

1、关于并行与并发的问题引入:

问题一: 计算机是如何执行程序指令的?

问题二: 计算机如何模拟出并行执行的效果?

问题三: 真正的并行需要依赖什么?

2、计算机执行指令示意图

12 认识进程与线程 (进阶) Python 第1张

12 认识进程与线程 (进阶) Python 第2张

2、轮询调度实现并发执行

并发:看上去一起执行,同时在发生

并行:真正一起执行,同时在进行

调度算法:

​ 时间片轮转

​ 优先级调度

12 认识进程与线程 (进阶) Python 第3张

3、并行需要的核心条件

​ 并行真正的核心条件是有多个CPU

12 认识进程与线程 (进阶) Python 第4张

阶段二:多进程实现并行

1、多进程并行问题引入

问题一: 什么是进程?

问题二: 如何在Python中使用进程?

问题三: 多进程实现并行的必要条件是什么?

2、进程的概念

计算机程序是存储在磁盘上的可执行二进制(或其他类型)文件。

​ 只有把它们加载到内存中,并被操作系统调用它们才会拥有其自己的生命周期。

进程则是表示的一个正在执行的程序。

​ 每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据

操作系统负责其上所有进程的执行。

​ 操作系统会为这些进程合理地分配执行时间。

3、在Python中直接执行耗时函数
import time
​
print('main-task start:', time.asctime(time.localtime(time.time())))
​
def func():
    print('sub-task start:', time.asctime(time.localtime(time.time())))
    time.sleep(5)
    print('sub-task end:', time.asctime(time.localtime(time.time())))
​
func()
time.sleep(5)
print('main-task end:', time.asctime(time.localtime(time.time())))
4、在Python中使用进程来分担耗时任务
import time
import multiprocessing
​
def func(n):
    for i in range(n):
        for a in range(n):
            for b in range(n):
                print(b)
​
start_time = time.time()
​
p = multiprocessing.Process(target=func, args=(50, ))    # 实例化,创建一个进程
# 参数如何传?  args=(50, )  kwargs={'n': 50}
p.start()   # 开启进程
p.join()    # 主进程等待子进程结束
​
func(50)
# func(50)
​
end_time = time.time()
print('运行了%ds!' % (end_time - start_time))
5、多进程并行的必要条件

总进程数量不多于CPU核心数量!

​ 因此,现在运行的程序都是轮询调度产生的并行假象。但是在Python层面的确获得了并行!

阶段三:多线程实现并发

1、多线程并发问题引入

问题一: 什么是线程?

问题二: 如何在Python中使用线程?

问题三: 为什么多线程不是并行?

2、线程的概念

线程被称作轻量级进程。

​ 与进程类似,不过它们是在同一个进程下执行的。并且它们会共享相同的上下文。

当其他线程运行时,它可以被抢占(中断)和临时挂起(也成为睡眠)— 让步

​ 线程的轮询调度机制类似于进程的轮询调度。只不过这个调度不是由操作系统来负责,而是由Python解释器来负责。

3、在Python中使用线程来避开阻塞任务
import time
import multiprocessing
import threading
​
print('---outer--start---:', time.asctime(time.localtime(time.time())))
​
def func():
    print('---inner--start---:', time.asctime(time.localtime(time.time())))
    time.sleep(5)
    print('---inner--end---:', time.asctime(time.localtime(time.time())))
​
"""
在进程里可以模拟耗时任务,但是在线程里只能模拟阻塞任务,不能模拟耗时任务。因为多线程只有一个核心进程。
"""
p = multiprocessing.Process(target=func)    # 创建子进程
t = threading.Thread(target=func)   # 创建子线程
t.start()   # 开启子线程
​
time.sleep(5)
print('---outer--end---:', time.asctime(time.localtime(time.time())))

CPU在任意一个进程里,任意时刻,只能执行一个线程

​ 对进程的轮询是操作系统负责调度

​ 对线程的轮询是Python解释器负责调度

4、GIL锁 全局解释器锁

Python在设计的时候,还没有多核处理器的概念。

因此,为了设计方便与线程安全,直接设计了一个锁。

这个锁要求,任何进程中,一次只能有一个线程在执行。

因此,并不能为多个线程分配多个CPU。

所以Python中的线程只能实现并发,

而不能实现真正的并行。

但是Python3中的GIL锁有一个很棒的设计,

在遇到阻塞(不是耗时)的时候,会自动切换线程。

5、GIL锁带给我们的新认知

遇到阻塞就自动切换。因此我们可以利用这种机制来有效的避开阻塞~充分利用CPU

阶段四:使用多进程与多线程来实现并发服务器

关键点一: 多进程是并行执行,

​ 相当于分别独立得处理各个请求。

关键点二: 多线程,虽然不能并行运行,

​ 但是可以通过避开阻塞切换线程

​ 来实现并发的效果,并且不浪费CUP

from socket import *
from multiprocessing import Process     # 进程
from threading import Thread    # 线程
# 创建套接字
server = socket()
server.bind(('', 9999))
server.listen(1000)
​
# 定义函数
def func(conn):
    while True:
        recv_data = conn.recv(1024)
        if recv_data:
            print(recv_data)
            conn.send(recv_data)
        else:
            conn.close()
            break
​
​
while True:   # 循环去监听
    conn, addr = server.accept()
    # 每生成一个对等连接套接字,我就生成一个进程、线程,并且我让这个进程、线程去服务这个连接过来的客户端
# p = Process(target=func, args=(conn, ))    # 生成一个进程
    # p.start()   # 启动进程
​
​
    t = Thread(target=func, args=(conn, ))    # 生成一个线程
    t.start()   # 启动线程
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄