无栈协程库——protothread ProtoThread源码如下所示: #define LC_INIT(s) s = 0; #define LC_RESUME(s) switch(s) { case struct pt { lc_t lc; }; /** 初始化一个protothread实例,无栈协程实例,核心就是将指令标签设置为0 */ #define PT_INIT(pt) LC_INIT ,而外层用name->RoleData的映射关系管理协程及其他协程中间态数据; 需要注意的是——以protothread来说: ·对于无栈协程来说,因为不存在指针等信息,所以无栈协程的所有信息是可以缓存在共享内存的 ,以避免因为逻辑修改导致协程不可恢复的场景); 对于无栈协程来说,执行流的恢复只是通过找到下一条指令的执行地址,但是不包括上下文,这意味着无栈协程里面不能有局部变量,需要我们手动把后面需要用到的局部变量缓存起来 此外这里无栈协程是通过switch-case实现的,嵌套的switch-case会产生问题,限制比较多,所以也不适用于线上场景。
当前很多的编程语言都内置协程特性或者有自己的协程库,如C/C++的libco、golang的goroutine等。而在实现机制上,又可以划分为有栈协程和无栈协程,我们分别进行介绍。 ,然后把执行权限通过接口co_swap交还给父协程即可,并将当前协程退栈 无栈协程 有栈协程是基于函数切换上下文恢复的思路实现被中断协程的继续执行,但是这个上下文里面有返回地址,即下一条指令的地址 虽然我们可以对上述进行优化和封装,但是在这我们并不准备过多赘述,后面我们则直接看一个开源的无栈协程库-protothread 无栈协程库——protothread ProtoThread源码如图所示: ,而外层用name->RoleData的映射关系管理协程及其他协程中间态数据 需要注意的是——以protothread来说: 对于无栈协程来说,因为不存在指针等信息,所以无栈协程的所有信息是可以缓存在共享内存的 ,以避免因为逻辑修改导致协程不可恢复的场景) 对于无栈协程来说,执行流的恢复只是通过找到下一条指令的执行地址,但是不包括上下文,这意味着无栈协程里面不能有局部变量,需要我们手动把后面需要用到的局部变量缓存起来
无栈协程 有栈协程是基于函数切换上下文恢复的思路实现被中断协程的继续执行,但是这个上下文里面有返回地址,即下一条指令的地址,所以当程序发生改动重新编译生成,指令地址有可能发生改变,这种对于需要重新编译生成发布的发布场景支持并不友好 : 图片 在C语言中,switch实际上是一个转移表,而case则是一个标签——用于给一个或者一组指令进行命名,而标签本身并不会改变指令的控制流,而只是提供了一个程序的执行位置;基于此,形成无栈协程一个朴素的思想 无栈协程的Demo实现 一个协程库要解决以下几个问题: 1)如何在协程阻塞调用时归还执行权限? 2)如何选择合适的协程进行调度? 在前面讨论中,可以认为协程是一个函数的调用,那么协程的恢复无非是从调用中断处继续执行,而对于无栈协程不需要进行上下文恢复,则核心是通过存储标签保证下次调度能从预期的地方继续执行,那么就有: 1) 虽然我们可以对上述进行优化和封装,但是在这我们并不准备过多赘述,后面我们则直接看一个开源的无栈协程库-protothread 未完待续...
而golang在语言级实现的协程是独立栈的协程。 独立栈必然要为每个协程分配栈空间的内存,golang 1.4开始协程栈的大小是2kb,2kb可能对某些协程很浪费,对某些协程又完全不够;协程太多必然也导致分配和GC方面的压力。 之前一直对无栈协程关注不够,认真学一下后,做了如下总结,然后自己写一些代码来模拟无栈协程的运作方式: 无栈协程有这样一些特点: 无栈协程本质上是generator(生成器),执行generator函数就像是频繁调用某个对象的方法 ;在ABC三条接口相互不依赖的情况下,我们尝试用无栈协程的方式并发的访问三条接口。 Have Fun,希望你后续能够愉快的使用无栈协程。:-)
由于Go协程的栈是Go运行时管理的,并分配于堆上,不由操作系统管理,所以我们先来看看协程栈的内存如何被Go运行时管理和回收的。本篇文章先从初步认识协程栈开始。 查看本系列完整内容请访问 https://tigerb.cn/go/#/kernal/ 前言 ---- 为了对协程栈有个初步的认识,我们先来回顾数据结构中栈的概念,再来看看内存栈的概念作用,最后我们再来通过对比进程中的栈内存和线程中的栈内存来对协程中的栈内存有个初步的认知 谁决定了代码在运行过程中,从栈空间分配或释放多少内存? 我们分别从「进程栈」和「线程栈」、「协程栈」视角看看以上两个问题。 进程栈 什么是进程栈? 谁决定了代码在运行过程中,从栈空间(线程栈)分配或释放多少内存? 答:同进程,编译器决定。 协程栈 什么是协程栈? 答:使用`go`关键字创建一个协程时,Go运行时从堆上分配一块连续内存作为协程的栈空间。 谁决定了协程栈的栈空间的大小范围?
# 一个简单的小爬虫,将3个页面的数据保存到data.html,对比协程和非协程的使用时间 """协程 1、通过urlopen获取数据 2、写入文件 3、使用三个页面,通过gevent.joinal执行 (协程会在IO阻塞处切换),用时短 4、在Windows系统,由于捕获IO较慢。
网管碎碎念:堆和栈都是编程语言里的虚拟概念,并不是说在物理内存上有堆和栈之分,两者的主要区别是栈是每个线程或者协程独立拥有的,从栈上分配内存时不需要加锁。 ; v1.4 — 将最小栈内存降低到了 2KB; 分段栈和连续栈 分段栈 Go 1.3 版本前使用的栈结构是分段栈,随着goroutine 调用的函数层级的深入或者局部变量需要的越来越多时,运行时会调用 连续栈 连续栈可以解决分段栈中存在的两个问题,其核心原理就是每当程序的栈空间不足时,初始化一片比旧栈大两倍的新栈并将原栈中的所有值都迁移到新的栈中,新的局部变量或者函数调用就有了充足的内存空间。 copystack会把旧栈里的所有内容拷贝到新栈里然后调整所有指向旧栈的变量的指针指向到新栈, 我们可以用下面这个程序验证下,栈扩容后同一个变量的内存地址会发生变化。 goroutine中的stack信息里记录的栈区内存边界计算出来的,然后用旧栈两倍的大小创建新栈,创建前会检查是新栈的大小是否超过了单个栈的内存上限。
文章目录 一、协程概念 二、协程作用 三、创建 Android 工程并进行协程相关配置 1、创建 Android 工程 2、配置协程环境 3、布局文件 4、异步任务代码示例 5、协程代码示例 6、完整代码示例 四、异步任务与协程对比 一、协程概念 ---- 协程 Coroutine 是 Kotlin 语言 中新出现的概念 , 在 Java 语言中没有 ; 协程 是 基于 线程 的 , 是 轻量级 线程 ; 二、协程作用 ---- 协程主要作用如下 : 处理耗时任务 : 耗时任务 通常需要 阻塞主线程 , 线程量级太重 , 耗时任务 推荐在协程中执行 ; 保证主线程安全 : 从主线程中 安全地调用可能会挂起的函数 包下的 Executor,ThreadPoolExecutor,FutureTask 取代 AsyncTask ; 三、创建 Android 工程并进行协程相关配置 ---- 1、创建 Android Project " 选项 , 创建工程 , 创建 Empty Activity ; 注意选择 Kotlin 语言 , Android Studio 会自动添加 Kotlin 语言支持 ; 2、配置协程环境
# 一个简单的小爬虫,将3个页面的数据保存到data.html,对比协程和非协程的使用时间 """非协程 1、通过urlopen获取数据 2、写入文件 3、使用三个页面,通过for循环执行(非协程会在IO
文章目录 一、SupervisorJob 协程 二、supervisorScope 协程作用域构建器函数 在上一篇博客介绍了 协程异常处理 【Kotlin 协程】协程异常处理 ① ( 根协程异常处理 | 自动传播异常 | 在协程体捕获异常 | 向用户暴露异常 | 在 await 处捕获异常 | 非根协程异常处理 | 异常传播特性 ) , 其中介绍了 协程中异常的传播特性 : 协程 运行时 , 产生异常 , 会将异常 传递给 父协程 , 父协程会执行如下操作 : ① 取消子协程 : 不仅仅取消产生异常的子协程 , 该父协程下所有的子协程都会取消 ; ② 取消父协程 : 将父协程本身取消 ; ③ 向父协程的父协程传播异常 : 继续将异常传播给 父协程的父协程 ; 这样就会导致 某个子协程一旦出现异常 , 则 兄弟协程 , 父协程 , 父协程的兄弟协程 , 父协程的父协程 等等 都会被取消 , 这样牵连太大 , 因此本篇博客中引入几种异常处理机制解决上述问题 ; 一、SupervisorJob 协程 ---- SupervisorJob 协程 执行时如果 该类型的 子协程 出现异常 , 不会将 异常传递给 父协程 , 因此也不会影响到 父协程 下的 其它子协程
协程的特点是利用任务的阻塞时间去处理其他任务 处理任务的是线程,而协程是单线程,占用资源由大到小排:多进程>多进程>协程 gevent模块封装greenlet模块,greenlet模块封装yield 在 gevent.sleep,或者使用monkey补丁实现替换 如代码因为monkey.patch_all()补丁问题报错,将from gevent import monkey和补丁代码放到最前面尝试 使用协程完成多任务三个例子 2---") g2 = gevent.spawn(func2, 5) print("---3---") g3 = gevent.spawn(func3, 5) print("---4---") # 协程的最大特点就是利用某个任务阻塞的时间去处理其他任务
1 协程 1.1协程的概念 协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。 那么这么来理解协程比较容易: 线程是系统级别的,它们是由操作系统调度;协程是程序级别的,由程序员根据需要自己调度。 比较专业的理解是: 协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。 1.2 协程的优缺点 协程的优点: (1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力) 实现协程程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。
4.Go 协程是什么? Go 协程是与其他函数或方法一起并发运行的函数或方法。Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。 for的子块,for执行完毕后,它占用的内存就会被释放,那么它的子块作用域也会被释放 go 协程的理论如下 启动一个新的协程时,协程的调用会立即返回。 与函数不同,程序控制不会去等待 Go 协程执行完毕。在调用 Go 协程之后,程序控制会立即返回到代码的下一行,忽略该协程的任何返回值。 如果希望运行其他 Go 协程,Go 主协程必须继续运行着。 如果 Go 主协程终止,则程序终止,于是其他 Go 协程也不会继续运行 注意 main 函数其实调用也是一个协程,它被称为 主协程 package main import "fmt" func print 信道可用于在其他协程结束执行之前,阻塞 Go 主协程。
文章目录 前言 一、协程库实现-独立栈 1.协程结构体定义 2.协程调度器定义 3.协程创建函数 4.协程启动/恢复函数 5.协程挂起函数 6.判断协程是否运行完毕函数 7.使用示例 二、协程库实现- 一、协程库实现-独立栈 通过独立栈实现的协程库中的每一个协程都有自己独立的栈空间,协程栈大小固定且互不干扰。 -共享栈 通过共享栈实现的协程库中的每一个协程在运行时都使用一个公共的栈空间,当协程挂起时将自己的数据从共享栈拷贝到自己的独立栈,协程运行时又将数据从独立栈拷贝到共享栈运行,本文是参考cloudyun大神代码进行简要分析 ; }; 和独立栈实现方式类似,每个协程内部都有一个栈指针stack,不同之处在于独立栈的空间大小固定,并且协程运行时数据存放在该栈空间。 而共享栈实现时协程运行时,数据存放在schedule_t结构体中的stack成员中,这个栈被所有协程共享。
文章目录 一、协程调度器 二、协程任务泄漏 三、结构化并发 一、协程调度器 ---- 协程 是在 调度器 中运行的 , 在协程中有 3 种调度器 : Dispatchers.Main 调度器 : 在 主线程 ---- 协程任务泄漏 : 发起 协程任务 后 , 无法追踪任务的执行结果 , 任务等于无效任务 , 但是仍然会消耗 内存 , CPU , 网络 , 磁盘 等资源 ; Kotlin 中引入了 结构化并发机制 避免 协程任务泄漏 的情况发生 ; 协程任务泄漏 与 内存泄漏 类似 ; 三、结构化并发 ---- 结构化并发 使用场景 : 协程任务取消 : 在不需要协程任务的时候 , 取消协程任务 ; 追踪协程任务 : 追踪正在执行的协程任务 ; 发出错误信号 : 如果 协程任务执行失败 , 发出错误信号 , 表明执行任务出错 ; 协程任务 运行时 , 必须指定其 CoroutineScope 协程作用域 , 其会追踪所有的 协程任务 , CoroutineScope 协程作用域 可以取消 所有由其启动的协程任务 ; 常见的 CoroutineScope 协程作用域 : GlobalScope : 该作用域是 进程级别的
阻塞协程是种特殊的协程启动方式,一般是用 runBlocking{} 扩起来一段协程。 首先是父协程得到执行,然后才是子协程。 重点是这两段协程都在同一个线程main里完成。这里就带来一个有趣的问题, runBLocking{}和平时常用的launch有什么区别? 在创建完coroutine后就进入派发流程了,这部分和Kotlin协程-一个协程的生命周期中的逻辑比较相似,下面也会讲到。 每个线程都可以起一个独立的阻塞协程队列。 这个问说明,runBLocking{}这种协程,它的运行逻辑是先把父协程放队列里,然后取出来执行,执行完毕再把子协程入队,再出队子协程,用同样的方式递归。
而有栈协程和无栈协程的实现, 差异最大的地方就是如下两点了: 1. 怎么保存和恢复当前的执行位置 2. 怎么保存和恢复当前协程引用到的内存(变量等) 本篇主要侧重无栈协程, 无栈协程相关的机制后续会具体展开. , 与boost.context这样的高性能有栈协程实现机制后, 标准委员会还会继续寻求无栈协程的解决方案, 并最终将其作为C++协程的实现机制呢, 这里分析主要的原因是为了解决有栈协程天然存在的限制: C++17中基于Duff Device Hack的无栈协程实现, 以及C++20中的无栈协程做更深入的介绍. 2. 但当时的GCC用的是8.3版本, 并不支持coroutine20, 所以我们最终采用的是一个基于C++17的无栈协程实现方案, 也就是使用前面介绍的Duff Device Hack方式实现的无栈协程.
而有栈协程和无栈协程的实现, 差异最大的地方就是如下两点了: 怎么保存和恢复当前的执行位置 怎么保存和恢复当前协程引用到的内存(变量等) 本篇主要侧重无栈协程, 无栈协程相关的机制后续会具体展开. libco, 与 boost.context 这样的高性能有栈协程实现机制后, 标准委员会还会继续寻求无栈协程的解决方案, 并最终将其作为 C++协程的实现机制呢, 这里分析主要的原因是为了解决有栈协程天然存在的限制 C++17 中基于 Duff Device Hack 的无栈协程实现, 以及 C++20 中的无栈协程做更深入的介绍. 2. 但当时的 GCC 用的是 8.3 版本, 并不支持 coroutine20, 所以我们最终采用的是一个基于 C++17 的无栈协程实现方案, 也就是使用前面介绍的 Duff Device Hack 方式实现的无栈协程 其中比较重要的一部分就是协程了, 当时引入协程的方式和目的都比较直接, 首先是使用 Duff Device Hack 的机制来实现整个无栈协程.
协程的定义: 协程通过在线程中实现调度,避免了陷入内核级别的上下文切换造成的性能损失,进而突破了线程在IO上的性能瓶颈。 协程和线程的关系 协程是在语言层面实现对线程的调度,避免了内核级别的上下文消耗。 python协程与调度 Python的协程源于yield指令。 和大多数语言一样,在 Python 中,协程的调度是非抢占式的,也就是说一个协程必须主动让出执行机会,其他协程才有机会运行。 让出执行的关键字就是 await。 (goroutines)和协程(coroutines) //Go 协程意味着并行(或者可以以并行的方式部署),协程一般来说不是这样的 //Go 协程通过通道来通信;协程通过让出和恢复操作来通信 // 当一个协程阻塞的时候,调度器就会自 动把其他协程安排到另外的线程中去执行,从而实现了程序无等待并行化运行。
Grunnable Golang中,一个协程在以下几种情况下会被设置为 Grunnable状态: 创建 Go 语言中,包括用户入口函数main·main的执行goroutine在内的所有任务,都是通过runtime