协程是什么

以下是我自己的理解:

一般的线程切换是由操作系统来执行的,

而协程则是一种特殊的线程,这种线程的切换是由用户自己来决定的,并且切换需要做的额外工作如:执行状态和执行位置的保存,也是由用户自己来做的。

以下是一个解释的比较清楚的内容:

笔者最美好的记忆来自于早年在6502 cpu的cc800上写汇编的年代, 那个时代的计算机甚至没有操作系统,也没有实模式等保护机制。在6502上写汇编应用其实非常简单,系统会把bin文件加载到一个固定的内存地址中,cpu会固定地从一个特定的位置开始执行。然后cpu就按照你提供的机器指令开始一条一条的执行。在高级语言中的“函数调用”的概念,在汇编里主要体现为两个寄存器。寄存器是cpu内部临时保存数据的区域,相当于高级语言里的变量。但是有一个寄存器是特殊的,它存放了cpu当前正在执行的指令的内存地址(Instruction Register)。一个高级语言中的函数一般会被编译成指令存放在一段连续的内存空间中(data segment)。那么所谓函数执行到了第几行这样的信息其实就是保存在这个Instruction Register中的。另外一个很特殊的寄存器是Stack Register,它其中存放的内存地址指向的内存区域用于函数之间传递参数和返回值,以及存放一个函数内的局部变量。如果不考虑现代计算机cpu中各种各样其他存放中间结果的寄存器,理论上保存了Instruction Register(执行到哪儿了)和Stack Register(堆栈上的变量)就保存了一个函数的当前执行状态,分别是函数当前执行到了哪,以及这个函数局部变量所代表的当前state。

  事实上,操作系统的几个关键切换也是这么来完成的。操作系统提供了两个执行态,一个是用户态,一般我们的代码都是执行在用户态的。另外一个是内核态,像驱动程序之类的代码会用各种方式被加载到操作系统内部执行在内核之中。内核态里的代码可以完全控制CPU的I/O中断,从而可以和外部设备交互。用户态的代码属于受限代码,必须把I/O请求通过syscall交由运行在内核态的操作系统来完成。当一个cpu的核在执行用户态代码时,其寄存器里存放的状态是你的应用的代码的状态,但是应用要进行I/O操作的时候,cpu要被切换到内核的代码里去执行内核态的代码。这里就需要进行一次context switch,所谓context switch其实原理不会比把寄存器的值存到内存的一个地方,等回来的时候再把内存中临时保存的值加载回寄存器复杂多少。

  操作系统还有一个需要进行context switch的地方,那就是在协程与协程之间。操作系统在执行一个ELF或者PE的可执行文件的时候,对于这个可执行文件内的汇编代码来说,整个内存寻址空间是独立的。也就是1.exe的执行状态完全无法感知到2.exe的执行状态的内存。也就是现代操作系统的虚拟内存空间。有cpu在两个进程之间切换状态的时候,需要把内存的映射关系调整过来,否则虚拟内存的地址是无法对应到正确的物理地址的。一个进程内的两个线成切换的时候,要稍微简单一些,只需要把当前线成正在执行的位置和栈做切换就可以了。

  无论是操作系统做user/kernel的switch,还是process/process,thread/thread的switch,其实现方式都是大同小异的。通过把“当前执行状态”这样的一个抽象概念落实为一个具体的数据结构存储起来,然后指挥cpu在不同的场合加载不同的数据恢复不同的“当前执行状态”。

  在高级语言中,一个函数正在执行的位置以及其状态,内部都可以有一个抽象的表达方式。有的高级语言直接被编译成原生的机器码,那么其执行状态的表述就和操作系统的context switch的context非常类似。有的高级语言自身执行在一个虚拟机之上,那么其context的表述可能是虚拟机的instruction register和stack register,而不是80x86这样原生的机器的物理寄存器。但是原理是非常类似的。

  取决于语言设计者的觉悟,有的语言会把这种表达执行状态的能力直接提供出来,让一个函数在执行过程中可以把当前状态保存,然后把执行权交给另外一个函数执行,等那个函数放弃执行权回来的时候再把保存的状态恢复。这也就是所谓的协程(co-routine)。协程与线程的区别在于,协程的context switch是在完全在用户态,由语言的runtime或者是库来完成的。而线程的context switch则是操作系统来完成的。

转自:https://segmentfault.com/a/1190000000663472

这里有一个点,需要指出,文章起名【协程是“用户态的线程”】,文中又有提到“一般我们的代码都是执行在用户态的”,那就是说我们随便创建一个简单的线程,只要没有一些类似IO的系统级操作,不需要系统级的切换,那么这个线程也是“用户态的线程”,或者说这个线程会运行在用户态下,但是这个线程不一定是协程。也就是说——用户态的线程包含协程,但用户态的线程并不一定就是协程。

额外啰嗦这些东西,希望能减轻一些人的疑惑。

再来一篇介绍Java中协程的文章:

https://www.oschina.net/question/2680454_2180396

时间: 2024-11-10 17:45:33

协程是什么的相关文章

关于Unity协程(Coroutine)

协程官方doc解释A coroutine is a function that can suspend its execution(yield) until the given given YieldInstruction finishes. StartCoroutine开启协程 先执行协程中的代码 碰到yield return时控制权交给unity引擎 引擎继续做接下来的工作例如第一次yield return之后执行StartCoroutine下一行代码 直到满足yield指令的要求才会重新进

Gevent的socket协程安全性分析

一般讨论socket的并发安全性,都是指线程的安全性...而且绝大多数的情况下socket都不是线程安全的.. 当然一些框架可能会对socket进行一层封装,让其成为线程安全的...例如java的netty框架就是如此,将socket封装成channel,然后让channel封闭到一个线程中,那么这个channel的所有的读写都在它所在的线程中串行的进行,那么自然也就是线程安全的了..... 其实很早看Gevent的源码的时候,就已经看过这部分的东西了,当时就已经知道gevent的socket不

PHP实现协程

在服务器编程当中,为了实现异步,经常性的需要回调函数,例如以下这段代码 function send($value) { $data = process($value); onReceive($data); } function onReceive($recv_value) { var_dump($recv_value); } function process($value) { return $value+1; } $send_value = 1; send($send_value); 实现的东

python并发编程之---协程

1.什么是协程 协程:是单线程下的并发,又称微线程,纤程. 协程是一种用户态的轻量级线程,协程是由用户程序自己控制调度的. 2.需要注意的点: 需要强调的是: #1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行) #2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关) 对比操作系统控制线程的切换,用户在单线程内控制协程的切换 优点

python协程有多厉害?

爬一个××网站上的东西,测算了一下协程的速度提升到底有多大,网站链接就不放了... import requests from bs4 import BeautifulSoup as sb import lxml import time url = 'http://www.××××.com/html/part/index27_' url_list = [] start = time.time() for i in range(2,47): print('get page '+str(i)) hea

进程、线程和协程的区别

进程: 进程之间不共享任何状态,进程的调度由操作系统完成,每个进程都有自己独立的内存空间,进程间通讯主要是通过信号传递的方式来实现的,实现方式有多种,信号量.管道.事件等,任何一种方式的通讯效率都需要过内核,导致通讯效率比较低.由于是独立的内存空间,上下文切换的时候需要保存先调用栈的信息.cpu各寄存器的信息.虚拟内存.以及打开的相关句柄等信息,所以导致上下文进程间切换开销很大,通讯麻烦. 线程: 线程之间共享变量,解决了通讯麻烦的问题,但是对于变量的访问需要锁,线程的调度主要也是有操作系统完成

爬虫协程比线程爬取速度更快?

先做个小示例,不用废话谈理论,没有实践的空谈都是扯蛋误导人. # coding=utf-8 import requests,time count=0 urlx= 'http://www.xxsy.net/' # 'http://www.danmeila.com/' http://www.sina.com.cn/ 'http://www.qingkan9.com/' # # 'http://www.qingkan9.com/' def fun(url): try: print url resp=r

【Unity笔记】协程Coroutine的简单优化

一个最简单的协程,也至少需要2帧才能完成.第一帧走到yield return null停止,第二帧从此处接着执行完下面的操作.需求:如果缓存中存在某数据则直接使用,否则联网异步下载. private bool cached; // 该数据是否已有缓存 void Start(){ StartCoroutine(Download()); } IEnumerator WorkWhenDownload() { if(cached){ // 直接使用缓存 }else{ // 没有缓存,联网下载 WWW w

【Unity笔记】使用协程(Coroutine)异步加载场景

using UnityEngine; using System.Collections; using UnityEngine.SceneManagement; using System; public class LoadingPage : MonoBehaviour { public UISlider progressBar; // 目标进度 float target = 0; // 读取场景的进度,取值范围0~1 float progress = 0; // 异步对象 AsyncOperat

10 线程 协程 socketserver 基于udp的socketserver

线程进程 操作系统的作用: 1.把硬件丑陋复杂的接口隐藏起来,为应用程序提供良好接口 2.管理,调用进程,并且把进程之间对硬件的竞争变得有序化 多道技术: 1.产生背景:为了实现单cpu下的并发效果 2.分为两部分: 1:空间上的复用(必须实现硬件层面的隔离) 2:时间上的复用(复用cpu的时间片) 什么切换? 1:正在执行的任务遇到的阻塞 2:正在执行的任务运行时间过长 进程:正在运行的一个过程/一个任务,由操作系统负责调用,然后由cpu负责执行程序:就是程序员写的代码并发:伪并行,单核+多道