关于协程:漫谈协程coroutine

一 什么是协程协程当初曾经不是一个新的技术了,然而因为之前始终在用较低版本的c++,没什么机会应用协程。最近写了不少go的代码,接触到了协程,所以想从零开始学习一下协程。 1. 到底什么是协程之前据说协程的时候,大家都讲协程就是执行在用户态的微线程,加上go中协程的应用和线程差不多,我也就始终这样了解了。然而真正定义协程的性能是:能够随时的挂起和复原,它容许多个入口点在不同的执行点挂起和复原,而不是像线程那样在任何时候都可能被零碎强制切换。那么能够随时挂起和复原到底能解决什么问题呢?上面咱们来谈谈协程的劣势。 2. 协程的劣势协程领有轻量,高效,简略等劣势。 轻量:协程个别都是在各个语言的层面上做实现,线程依然是操作系统运算调度的最小单位,比起线程来,创立协程更加轻量。协程有多种实现形式,当咱们在一个线程上调配多个协程时,协程之间就不须要思考锁机制。高效:当咱们的线程在执行IO密集型操作时,往往须要期待IO后果,此时操作系统要么做线程的切换,而频繁的切换线程是一个和高额的操作,当应用协程的时候,咱们在线程内应用协程将操作挂起,期待IO实现时再继续执行,这样不会产生线程切换等操作。简化异步编程:在咱们应用rpc框架时,框架往往会提供同步,异步等调用形式,当同步调用其余接口时,以后线程会被阻塞,当异步调用其余接口时,就须要你提供一个回调函数,当有后果返回时,由框架将后果回吐给你。这种编程形式是不不便的,协程能够简化这个操作,前面咱们会举例说明。上面我会介绍协程是如何产生上述劣势的,行文逻辑如下,在第二个章节,我会介绍,当已知了协程的性能,应用协程的时候,咱们如何简化了异步编程;第三个章节咱们会介绍协程是如何实现咱们心愿的那些性能的。 二 应用协程异步编程应用异步做网络编程(实现业务逻辑)时,咱们的业务代码是有严格的执行程序的,然而异步的返回是无序的,就使得咱们,代码往往须要一些状态码来判断前置调用是否曾经实现,如果再叠加了异样解决这些逻辑的话,代码逻辑会十分艰涩难懂,而且容易经常性的造成回调天堂。举个例子,如果咱们应用异步回调的形式对一个整型数字做加3的操作,咱们有一个加1的函数,加3时须要调用三次: void AsyncAddOne(int val, std::function<void (int)> callback) { std::thread t([value, callback = std::move(callback)] { callback(val + 1); }); t.detach();}AsyncAddOne(1, [] (int result) { AsyncAddOne(result, [] (int result) { AsyncAddOne(result, [] (int result) { cout << "result is: " << result << endl; }); }); });看起来非常的艰涩难懂,当初大部门的服务框架其实曾经做了一些优化,比方应用Promise/Future个性。上面只是简略示意一下: AddOne.then({return AddOne.then({return AddOnde})})咱们拿一个在日常生产过程中的一段实例来示范Promise/Future个性,示例如下:这段代码的逻辑是应用了两个异步线程别离调用了redis和mysql,拿到后果后做本身的业务解决申请 // 第一个串行工作,CommonTasktrpc::Future<Result> CommonHandler() { // 1. do something in common handler return MakeReadyFuture<Result1>(res);}void HttpHandler() { // 1. 解决公共逻辑 auto http_task = CommonHandler(); // 2. 工作1实现后,创立并执行并行任务 auto data_task = http_task.Then([](Future<Result1>&& result1) { // 2.1 创立redis工作,通过redis_proxy发动调用, 并返回相干后果,cmd为申请redis的命令 trpc::Future<Result2> fut_redis_task = redis_proxy->AsyncRedis(cmd); // 2.2 创立mysql工作, 通过mysql_proxy发动调用, 并返回相干后果,cmd为申请mysql的命令 trpc::Future<Result2> fut_mysql_task = mysql_proxy->AsyncMysql(cmd); // 将单个工作退出parallel_futs parallel_futs.push_back(fut_redis_task); parallel_futs.push_back(fut_mysql_task); // 若并行任务2.1和2.2都实现了则完结该回调,并进入下一个回调 auto fut = WhenAll(parallel_futs).Then([](std::vector<Future<Result2>>&& result2) { // 别离取得redis和mysql的result, 进而实现相干工作 // result[0].GetValue(); // result[1].GetValue(); // 3. do something calc handler... return trpc::MakeReadyFuture<Resul3t>(res); }); return fut; }); // 回包 data_task.Then([](Future<Result3>&& result3){ if (result3.IsReady()) { // 4. do something and response to client // full succ in reply } else { // full exception in reply } SendUnaryResponse(reply); // 链式调用最初的then能够返回void });}尽管Future这种模式曾经简化了之前本人写代码判断各个异步工作的实现状态(实际上是封装在了Future本身的逻辑中),然而也有肯定的编程复杂度,尤其在波及到错误处理的时候。应用协程能够让咱们像应用一个线程做同步调用一样,来写咱们的一部调用代码。具体是如何做到的,能够参照下文的实现。 ...

February 19, 2024 · 1 min · jiezi

关于协程:react17源码浅析

前言 React17自去年十月公布以来,呈现了几个比拟重要的变动。首先,17作为一个过渡版本,其明确了在react中的定位,即:承前启后,作为渐进式框架的首版本,在后续的18、19等版本中会进行渐进降级而不是强制进行硬切换;其次,17联合最新的浏览器的个性做了一些更改和优化,比方对合成事件零碎的优化;最初,自16以来的基于Fiber架构的模式对整个react性能优化在每个小版本中也会一直的进行逐渐的微调,每次的微调都走漏着react大佬们的一些思路与思考。本文以react17.0.0版本的源码动手,着重从react-reconciler和scheduler这两个模块中的局部源码进行拆解和浅析,心愿可能窥一斑而见全豹,揣度各位大佬的一些架构思路和想法,从而拓宽一些集体的眼界,然集体程度无限,不免洞若观火,对react的了解也可能有所偏颇,本着想将这样一个宏大的架构简洁剖析进去的想法,心愿能对各位同学有所启发,对于更为经典的局部,仍须要各位去品读源码,咱们依然应该对源码放弃着一颗敬畏之心,随着技术的晋升,每每品读都会有不同的感触! 目录构造 react目录比拟大,涉猎的也比拟多,这里只显示可能会波及局部的目录 packages react src jsx ReactJSX.jsReactJSXElement.js (定义了jsx)ReactJSXElementValidator.jsReact.jsReactBaseClasses.js (setState及forceState)ReactContext.js (context上下文及Provider和Consumer)ReactElement.js (定义了ReactElement的格局)ReactForwardRef.js (Ref的定义)ReactHooks.js (ReactHooks相干,不是本文重点,能够参看之前的文章)ReactLazy.jsReactMemo.jsReactMutableSource.jsReactStartTransition.js (批量更新的事务的概念)react-dom src clients ReactDOMHostConfig.jsReactDOMLegacy.jsReactDOMRoot.js (三种模式:legacy模式、blocking模式、concurrent模式)events EventListeners.jsReactDOMEventListeners.jsSyntheticEvent.jsserver (React Sever Component相干,不开展讲了)react-reconciler src ReactChildFiber.jsReactFiber.jsReactFiberBeginWork.jsReactFiberCommitWork.jsReactFiberCompleteWork.jsReactFiberLane.jsReactFiberReconciler.jsReactFiberRoot.jsReactFiberWorkLoop.jsscheduler src Scheduler.jsSchedulerMinHeap.jsSchedulerPostTask.jsSchedulerProfiling.js源码解析整个React的Fiber架构的外围在于对浏览器进行了工夫分片解决(ps:Firefox新版本本身也实现了工夫分片),抹掉了平台的差别,从而使得浏览器解决时候能够将控制权交出去,防止了js线程过多占用而阻塞渲染线程,实现了更细粒度的调度,即为:协程或纤程的调度 React文件名作用备注jsx-runtime.jsjsx解释器编译jsxReactElement.jsReact元素的格局React的结点格局信息jsx-runtime.jsreact17之后不再须要对每个react组件进行react的import,其内置了一个jsx-runtime的运行时,感兴趣的同学能够看一下这个react-jsx-dev-runtime.development.js,简略来说就是利用正则对jsx进行了一层浅的转化,实质jsx是对js的一种扩大 ReactElement.jsconst ReactElement = function( type, key, ref, self, source, owner, props) { const element = { $$typeof: REACT_ELEMENT_TYPE, type: type, key: key, ref: ref, props: props }}React-DOM这里次要对合成事件及DOM的一些解决进行了论述 <h2>DOM解决</h2> 文件名作用备注ReactDOMHostConfig.jsappendChildToContainer等原生dom操作ReactDOMLegacy.jslegacyRenderSubtreeIntoContainer未启用异步渲染的dom操作ReactDOMRoot.jscreateRoot、createLegacyRoot、createBlockingRoot三种模式的根组件React17的进行了模式的设置,别离为:Legacy模式、Concurrent模式、Blocking模式,其中Concurrent模式是启用fiber分片的异步渲染形式,而Legacy模式则仍是15的同步渲染模式,Blocking则是介于二者之间的模式,React无意依照这样一种渐进的形式进行适度 <h2>合成事件</h2> 文件名作用备注EventListeners.jsaddEventCaptureListener、addEventBubbleListener原生事件监听ReactDOMEventListeners.jsdispatchEventReact的事件SyntheticEvent.jscreateSyntheticEvent合成事件ReactDOMEventListeners.js外围是dispatchEvent进行事件的散发,17之后不再将事件全副冒泡到document去代理,这和浏览器的改良无关,不再须要代理绑定,浏览器能够对更细粒度的区域进行监听 function dispatchDiscreateEvent() {}function dispatchBlockingEvent() {}export function dispatchEvent( domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, targetContainer: EventTarget, nativeEvent: AnyNativeEvent,): void { if ( allowReplay && hasQueuedDiscreteEvents() && isReplayableDiscreteEvent(domEventName) ) { queueDiscreteEvent( null, // Flags that we're not actually blocked on anything as far as we know. domEventName, eventSystemFlags, targetContainer, nativeEvent, ); return; } const blockedOn = attemptToDispatchEvent( domEventName, eventSystemFlags, targetContainer, nativeEvent, ); if (blockedOn === null) { // We successfully dispatched this event. if (allowReplay) { clearIfContinuousEvent(domEventName, nativeEvent); } return; } if (allowReplay) { if (isReplayableDiscreteEvent(domEventName)) { queueDiscreteEvent( blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent, ); return; } if ( queueIfContinuousEvent( blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent, ) ) { return; } clearIfContinuousEvent(domEventName, nativeEvent); } dispatchEventForPluginEventSystem( domEventName, eventSystemFlags, nativeEvent, null, targetContainer, );}Scheduler实质是依据工作开始工夫和过期工夫利用小顶堆的优先队列而进行的工夫分片解决及调度 ...

March 9, 2021 · 31 min · jiezi

关于协程:CC协程学习笔记丨CC实现协程及原理分析视频

协程,又称微线程,纤程。英文名Coroutine。 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中失去广泛应用。 子程序,或者称为函数,在所有语言中都是层级调用,比方A调用B,B在执行过程中又调用了C,C执行结束返回,B执行结束返回,最初是A执行结束。 所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。 子程序调用总是一个入口,一次返回,调用程序是明确的。而协程的调用和子程序不同。 协程看上去也是子程序,但执行过程中,在子程序外部可中断,而后转而执行别的子程序,在适当的时候再返回来接着执行。 留神,在一个子程序中中断,去执行其余子程序,不是函数调用,有点相似CPU的中断。比方子程序A、B:def A(): print '1'print '2'print '3'def B():print 'x'print 'y'print 'z'假如由协程执行,在执行A的过程中,能够随时中断,去执行B,B也可能在执行过程中中断再去执行A,后果可能是: 12xy3z然而在A中是没有调用B的,所以协程的调用比函数调用了解起来要难一些。 看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那和多线程比,协程有何劣势? 最大的劣势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序本身管制,因而,没有线程切换的开销,和多线程比,线程数量越多,协程的性能劣势就越显著。 第二大劣势就是不须要多线程的锁机制,因为只有一个线程,也不存在同时写变量抵触,在协程中管制共享资源不加锁,只须要判断状态就好了,所以执行效率比多线程高很多。 因为协程是一个线程执行,那怎么利用多核CPU呢?最简略的办法是多过程+协程,既充分利用多核,又充分发挥协程的高效率,可取得极高的性能。 Python对协程的反对还十分无限,用在generator中的yield能够肯定水平上实现协程。尽管反对不齐全,但曾经能够施展相当大的威力了。 来看例子: 传统的生产者-消费者模型是一个线程写音讯,一个线程取音讯,通过锁机制管制队列和期待,但一不小心就可能死锁。 如果改用协程,生产者生产音讯后,间接通过yield跳转到消费者开始执行,待消费者执行结束后,切换回生产者持续生产,效率极高:import time def consumer():r = ''while True:n = yield rif not n:returnprint('[CONSUMER] Consuming %s...' % n)time.sleep(1)r = '200 OK'def produce(c):c.next()n = 0while n < 5:n = n + 1print('[PRODUCER] Producing %s...' % n)r = c.send(n)print('[PRODUCER] Consumer return: %s' % r)c.close()if __name__=='__main__':c = consumer()produce(c)执行后果: ...

October 26, 2020 · 6 min · jiezi