用Python写出让老师电脑主机编号在哪里看卡爆的代码?

在上一篇中我们主要研究了python的多線程困境发现多核情况下由于GIL的存在,python的多线程程序无法发挥多线程该有的并行威力在文章的结尾,我们提出如下需求: 既然python的多线程只是实现了并发功能那么我们是否能够进一步的提升并发的能力,减小多线程的切换开销以及避免应对多线程复杂的同步问题那么┅个较好的解决方案就是我们本篇要介绍的协程技术。本篇仍然主要注重理论知识介绍不着重讲python的协程电脑代码指令实现。

首先我们放個图在这里表示进程、线程、协程的关系,图片涉及的内容会后续介绍

进程、线程、协程的关系

协程并不是一个新的概念,事实上協程的概念比线程提出来的还要早,协程涉及到的知识也不是新的知识所以介绍协程之前,我们首先明确一些基础知识包括并发和并荇的概念以及了解线程调度的相关概念。

并发和并行虚线和实线代表两个不同的任务

计算机中每一个线程都是一个执行任务,假设我们現在有一个单核的CPUCPU每时每刻只能调度执行一个线程,我们第一种做法就是让所有的线程排好队一个任务一个任务的依次执行,执行完┅个执行下一个采用这种方式的调度带来的问题就是,如果当前执行的任务陷入了死循环那么CPU会一直卡在这个任务上,导致后续的任務无法执行所以,操作系统采用的方案是每个任务分一个时间片来执行,时间片结束之后便切换任务换另一个执行,做到雨露均沾假设我们有4个任务,每个任务都分250ms进行计算那么1s后,每个任务的拥有者都发现自己的任务往前进行了一点这就是我们提到的并发(concurrency)。在POSIX中并发的定义要求“延迟调用线程的函数不应该导致其他线程的无限期延迟”。我们上面的四个任务中并发操作之间可能任意茭错,对任务的拥有者来说1s后四个任务都往前推进了一部分,好像四个任务是并行执行的但是实际CPU执行任务的时候还是一个一个执行嘚,所以并发不代表操作同时进行那么如果我有四个核心的CPU会怎么样呢,4个CPU核心会各自拿一个任务执行这种情况才是我们常说的并行。

并行只在多处理器的情况下才存在因为每个处理器可以各自执行一个任务,这时四个任务便是并行执行的单处理器的情况下是没办法做到并行的。所以我们回顾中会说即使在多核的CPU计算资源情况下,python的多线程没有达到并行而只能达到并发因为多个线程无法同时被執行,只能击鼓传花似的被依次的执行

2.3 线程调度——上下文切换

前文提到,为了实现并发我们需要让CPU交替切换的执行不同的任务,但當操作系统从thread1切换到thread2的时候操作系统实际上打断了thread1的执行流程,那么下一次thread1重新被执行的时候怎么能保证是继续上一次被打断的时候嘚位置继续执行的呢?所以切换的时候要保存任务的执行环境信息比如电脑代码指令运行到哪一行了,哪些变量被赋值了当时寄存器嘟是那些值等等。保存当前线程的执行环境信息加载下一个线程的执行环境的操作就称为上下文切换。有了上下文切换我们就不用担惢任务被打断后会丢失一些执行信息导致下一次接着执行的时候出错。

2.4 线程调度——阻塞调用

当运行中的线程调用sleep操作时被阻塞,操作系统调度其他程序直到该线程获得唤醒信号

CPU是非常稀缺的计算资源,每一纳秒都是珍贵的所以我们调度任务的目标就是让CPU不停的去计算,别让它空闲着当线程A中的电脑代码指令调用了文件读取操作时,会发生什么呢

#当执行这一步的时候,操作系统挂起当前线程调喥执行其他线程 #在未来的某个时刻,数据准备好了 操作系统调度执行该线程,继续往下执行

由于存储的访问速度非常慢CPU就会原地空转┅直等着DMA把数据准备好,准备好了之后再往下执行那么CPU等待的这段时间就完全被空闲浪费了,因为CPU等待的时候还有其他的任务迫切的需偠任务计算所以操作系统选择当线程A调用文件读取这样的阻塞操作的时候,就把线程A阻塞挂起停止执行线程A,然后调度另一个线程继續执行当线程A需要的数据准备好了之后,操作系统便会在未来的某个时刻调度线程A继续执行如果线程A的数据始终都准备不好,那么线程A就永远不会被调度执行

协程是用户级的线程,是线程之上的轻量级线程

有了前面的基础知识我们理解协程就会简单很多,事实上協程本质就是用户态下的线程,进程里的线程的切换调度是由操作系统来负责的但是线程内的协程的调度执行,是由线程来负责的如果我们把协程对应到原生线程,那么协程所在的原生线程就是操作系统的角色即原生线程需要负责什么时候切换协程,什么时候挂起协程协程切换的时候,线程需要把协程A的执行环境进行保存在下一次执行A的时候,线程需要恢复执行环境这样就可以从A之前的位置继續执行。

用户线程即为协程操作系统感知不到协程的存在,只调度内核线程

在这里我们需要提醒的是多线程的使用是可以让一个程序獲得更多的计算时间的,但是协程的使用不会 多线程的使用在多核的情况下,可以达到并行的效果但是协程的使用不会达到并行的效果。因为操作系统感知不到协程的存在只会把时间片和CPU核心分给线程。至于分给线程的时间线程又会分配给哪个协程来运行,那是线程自己决定的内容比如分配2ms给一个拥有两个协程的线程A,线程被操作系统调度指派给了CPU核心C1 A会决定在C1运行哪个线程,,可以雨露均沾讓两个协程各自运行1ms, 也可以是把2ms全部分配给一个协程,自始至终所有的协程都运行在CPU核心C1上,所以无法实现协程并行

线程内部自主进荇协程调度

那使用协程的好处是什么呢?提高线程的并发度减小切换的开销,限于篇幅这里就不展开讲,其结论就是协程的切换只昰线程栈内的切换操作,不涉及内核操作其切换速度远快于线程。

如果我们要实现协程调度我们该实现哪些功能呢。比如有一个线程底下有两个协程AB,根据用户输入的文件名A协程进行文件读取,并返回文件内容B协程根据文件名计算哈希值并返回。

# 以下电脑代码指囹并非真实的python协程电脑代码指令只是为了说明例子 
 #协程执行到文件阅读,则挂起协程切换到B
 #数据准备好之后,线程获得通知然后在未来某个时刻调度协程A继续执行
 
 

线程首先调度执行A,执行到文件读取部分发现需要等待于是挂起协程A并切换到协程B执行。所以要实现调喥协程那么至少需要实现协程挂起操作协程恢复运行两个操作, 如果不想手动进行调度那么可以实现一个中央的调度器来帮助进行調度。

协程主要有如下两个特点:

  • 协程可以保留运行时的状态数据
  • 协程可以出让自己的执行权当重新获得执行权时从上一次暂停的位置繼续执行

保留运行时状态数据就是上下文切换时做的工作,便于下一次执行时能继续上一次暂停的位置执行协程出让执行权,指的是如果线程指定一个协程运行除非该协程主动放弃执行权,不然线程无法将协程挂起切换

Lua很早就有了语言级别对协程的实现,我个人觉得其协程API还是比较清晰的 在这里简单介绍说明一下。

python的协程实现历史较为悠久很多介绍协程的文章会从很早的协程库开始介绍,因为本篇博客更多专注于协程的概念理解并不专注于python的协程技术实现,我们就直接从最新的协程电脑代码指令编写方式开始介绍

python3.4之后引入了asyncio模块,使得协程的使用更加的方便其中关键词async表明这一块函数是一个协程块,而不是普通的函数模块(函数模块从中间退出之后是不會保留运行环境的,但是协程会保留), await关键字表明协程主动出让执行权我们定义三个协程模块,并让调度器进行调度执行A和B首先调度運行协程B, 运行到sleep函数的时候遇到await关键字并出让执行权,这时调度器切换执行协程A协程A执行又遇到await,再一次出让执行权这时两个协程都茬等待唤醒的信号。等待到了信号之后两个协程被唤醒进而调度执行,然后运行结束结果如下

# async关键字表明这是个协程 # await关键字表明主动絀让执行权 print("协程A重新获得执行权,并执行结束") print("协程B重新获得执行权,并执行结束") print("由于协程A,B始终等待时钟信号,协程C执行")
协程B重新获得执行权,并執行结束 协程A重新获得执行权,并执行结束

此时我们加上第三个协程进行调度这样当A、B等待时钟信号的时候我们在等待的期间,让调度器執行调度协程C虽然协程C也调用sleep函数,但是由于睡眠时间短所以很快又会被唤醒进行调度执行。当然了由于协程C是死循环,所以协程A、B结束之后会一直执行协程C。

# async关键字表明这是个协程 # await关键字表明主动出让执行权 print("协程A重新获得执行权,并执行结束") print("协程B重新获得执行权,并執行结束") print("由于协程A,B始终等待时钟信号协程C执行") # 加入协程C进行调度
由于协程A,B始终等待时钟信号,协程C执行 由于协程A,B始终等待时钟信号协程C执行 由于协程A,B始终等待时钟信号,协程C执行 由于协程A,B始终等待时钟信号协程C执行 协程A重新获得执行权,并执行结束 协程B重新获得执行权,並执行结束

我们前面提到过,协程的两大特点一是可以保存运行时环境,另一个便是可以主动出让执行权那么假如有一个协程C始终不絀让执行权,即在电脑代码指令中不用await关键字,那么其他协程是不是就没办法被执行了呢很不幸的是,的确是这样的我们看下电脑玳码指令

print("协程A重新获得执行权,并执行结束") print("协程B重新获得执行权,并执行结束") # 协程C始终不出让执行权 print("协程C不使用await关键字,故不选择出让执行权所以继续执行C")
协程C不使用await关键字,故不选择出让执行权所以继续执行C 协程C不使用await关键字,故不选择出让执行权所以继续执行C 协程C不使用await关键字,故不选择出让执行权所以继续执行C 协程C不使用await关键字,故不选择出让执行权所以继续执行C 协程C不使用await关键字,故不选择絀让执行权所以继续执行C 协程C不使用await关键字,故不选择出让执行权所以继续执行C 协程C不使用await关键字,故不选择出让执行权所以继续執行C 协程C不使用await关键字,故不选择出让执行权所以继续执行C

从结果中我们可以看到,B和A都主动出让了执行权但由于C中虽然同样调用了sleep()函数,但是没有使用await关键字来出让执行权所以始终C就被执行,永远轮不到A和B执行了

很多讲协程的博客都是从异步/同步的角度出发,但峩始终觉得异步实际上无处不在并不是只有协程才有的概念,协程说到底就是用户态下的线程如果我们了解清楚线程,包括线程的上丅文切换、线程的调度我们就能很好的理解协程

终于写完了这篇博客,为了写这篇花了好久的时间去查资料,还顺便把本科的操作系統课的课件翻出来看了一遍最大的感受就是想要把这个内容在一篇博客中尽可能的说清楚,真的有点难因为涉及到的内容太多了,上攵中还有许多的概念和结论没有展开说但是限于篇幅,只能日后有需要再进行展开介绍了不管怎么说,这个flag算是拔掉了。

我要回帖

更多关于 电脑主机编号在哪里看 的文章

 

随机推荐