怎么理解Kotlin协程

这篇文章主要讲解了“怎么理解Kotlin协程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解Kotlin协程”吧!

创新互联是一家专业提供湛江企业网站建设,专注与成都做网站、成都网站制作、H5网站设计、小程序制作等业务。10年已为湛江众多企业、政府机构等服务。创新互联专业网站设计公司优惠进行中。

协程的出现

咱们先来说说协程的历史,以及它是怎么混的这么惨的,毕竟悲催的人生都需要一个解释。

协程最早诞生于1958年,被应用于汇编语言中(距今已有60多年了),对它的完整定义发表于1963  年,协程是一种通过代码执行的恢复与暂停来实现协作式的多任务的程序组件。

而与此同时,线程的出现则要晚一些,伴随着操作系统的出现,线程大概在1967年被提出。线程作为由操作系统调度最小执行组件,主要用于实现抢占式的多任务。

既然大家都搞多任务,按说谁也不能比谁强多少啊,况且协程还早生几年,理论上通过自身努力发展,在编程语言中占据核心地位是极有可能的。

但是这协程的发展啊,一方面当然要靠自我奋斗,另一方面,也要考虑历史进程。而上个世纪七八九十年代,是计算机疯狂朝着小型化和个人化的方向演进的时代,计算机非常依赖操作系统来提供用户交互和压榨CPU的最大性能,而操作系统怎么来压榨计算机性能的呢?靠多线程。操作系统跟随个人计算机的普及之后,编程语言自然也开始依赖操作系统提供的接口来驾驭计算机了,线程成了几乎所有编程语言跳不过的一个重要概念,并一直延续至今。

到这里你可能要问了,大家都是搞多任务的,为什么线程能提升cpu的资源利用率,协程不能呢?

当然有很多其他的原因能解释,但最本质的原因仍然是协程和线程是有显著区别的两个概念,到这里我们就要回过头来聊聊什么叫协作式多任务,什么叫抢占式多任务?以及这两种任务是否是同一种概念?

  • 协作式多任务:

怎么理解Kotlin协程

上图是一个寿司生产的部分工序,我们可以把图中的传送转盘和机器抓手可视作两个任务,一起协作完成了食物的生产。这就是协作式多任务。协作式的多任务要求任务之间相互熟悉,才能实现协作。

  • 抢占式多任务:

怎么理解Kotlin协程

喂金鱼的场景,一把饲料下去,所有金鱼马上围上来一抢而空,这里每个金鱼都相当于一个任务线程,这就是抢占式多任务。而抢占式多任务(线程)之间不需要了解和配合,只有竞争关系。

上面两张图,比较生动的展示了协作式多任务(协程)和抢占式多任务(多线程)之间的区别。我们能够发现,协程更加适合那些相互熟悉的任务组件通过密切配合协作完成某些工作,协作式多任务里的“任务”是一种子程序(可称为函数)。抢占式多任务里的任务则是指能抢占资源的组件或代码(其实就是线程),这里的多任务也就是多线程。所以说,协程和线程本来是差异非常大的两种概念,他们的能力是不同的,而线程的这种能力正好迎合了那个时代的需求。自我奋斗+历史进程是线程成功的主要原因。

当然,在另一方面,也由于协程是基于编程语言层面的一种概念,它并没有统一定义的接口,因此在不同的语言中实现后的效果是不同的,这也会对开发者造成极大的困扰,不利于它的推广。而反观线程,通过操作系统的统一接口,定义了大体相同的线程使用方式,保证了不同的编程语言都对线程的使用是大体一致。

讲到这里,我们来总结下协程早期发展不顺的原因:

  1. 协程没有代表先进生产力的发展要求,先进文化的前进方向,和最广大开发者的根本利益[手动狗头]。

  2. 协程在不同编程语言中,它的实际表现有差异,非常不利于开发者的理解和使用。

以上两点,就是协程几十年以来一直不温不火的原因。我们也看到,虽然看起来都在搞多任务,但是协程和线程实际是没有太多交集的。

咸鱼翻身

虽说协程这种协作式多任务的组件不能提高程序执行的效率,似乎没有太广泛的应用前景,但这协程呐,也不能随意否定自己,因为不知道什么时候,你就突然被历史进程给关照了。

还是从线程说起,虽然线程成为编程世界的重要概念,但是在多年的使用过程中开发者们也逐渐意识到了它的痛点:

  • 线程之间(异步代码)难以交互难度比较大,往往只能用callback,大量的callback会代码难以阅读和理解,最终让项目变得难以维护。

简单说就是在开发者端,线程之间如何更方便的交互。

而这里协程能做什么呢?

或许我再重新表达一下线程的痛点:在开发者端,线程之间如何更方便的协作。

回想一下,我们是怎么介绍协程的?协作式多任务对吧,还记得上图中的转盘和机器抓手的协作么?我们当时说这两个任务更像是两个函数的协作,但如果把转盘和机器抓手视作两个线程呢?借助编译器,把线程封装成一个个能暂停和恢复的函数,线程是不是就可以像协程设计的那样协作呢?

我们还是从代码层面来看看如今协程是如何被使用的吧。设计一个简单的需求:社区内用户进行发帖时,需要先从后台验证发帖权限,请求两个接口,那么可能我们需要尝试开启两个线程先后来完成。

  • 普通的callback代码:

fun tryPost(){     // 先通过接口验证权限 (实际开启了一个线程,然后等待回调)     findUserPermission(user,callback()){         public void onSuccess(UserPermission response){             // 回调 如果成功 ,检查是否有权限             if(response.hasPermission){             // 如果有权限,则访问发帖接口 (同样开启线程,等待回调)                 postContent(content,callback()){                     public void onSuccess(Result response){                         // handle successful response                     }                     public void onFail(){                                              }                 }             }else{                 // 如果无权限,则......             }         }                  public void onFail(){          }     } }

这是比较常见的做法,我们需要访问两次接口,而交互只能在callback中进行,但是其实代码已经很难看了,如果还有其他的逻辑的话,那代码只会更加冗杂,难以维护。

那协程是怎么解决这种痛点的呢?我们看看(kotlin和python)协程的代码如何实现这种需求:

  • kotlin的协程代码

// 函数通过suspend关键字标识,可以被协程调用,具备暂停恢复的能力 ,实际上仍然使用了io线程来完成接口请求 suspend fun tryfindUserPermission():PermissionResponse {     return withContext(Dispatchers.IO){         findUserPermission(user)     } }  // 函数通过suspend关键字标识,可以被协程调用 suspend fun post():Result {     return  withContext(Dispatchers.IO) {         postContent(content)     } }      fun tryPost(){     //启动一个协程      launch{      //代码执行到这一行,让出cpu,进入暂停状态,等待请求成功之后,会恢复执行)      var response = tryfindUserPermission()    // 向后端访问用户权限      if(response.hasPermission){      // 有权限则开始发帖 (开启线程,让出cpu,暂停执行,等待恢复)         var response =  post()          // handle response if need     }   } }

可以看到,在kotlin中,协程通过把线程里的代码封装成一种能暂停/恢复的函数,让多线程之间的交互就像普通的函数一样简单,不需要callback。

  • python的协程代码

import asyncio // async 的关键字,表明这个函数可以被协程调用 async def findUserPermission():     // handle http request     ...     ...     return response async def postContent():     // handle http request     ...     ...     return response      async def main(): // 尝试获取权限信息 同样会让出cpu,进入暂停状态,等待恢复     reponse = await findUserPermission()     // 判断有权限的情况下,进行发帖     if response.hasPermission :         res = await postContent()         // handle response if need asyncio.run(main())

python通过协程处理这种问题本质和kotlin是一致的。

相信大家也能看到,协程在不同的语言中的表现方式是有差异的

通过上面几段伪代码,我们能够比较清楚看到,协程能非常明显的简化了线程之间协作复杂度,让我们可以以编写同步代码的方式来编写异步代码,极大的简化你的逻辑,让你的代码容易维护。

那么协程是如何做到的呢?

虽然不同的语言中,协程有所差异,但是原理都差不多,编程语言的编译器通过一些关键字(kotlin中用suspend,python中用async等)来修饰函数,在编译期间根据关键字生成一些线程相关的代码来实现函数的暂停恢复的功能,从而实现把线程相关的代码留在编译期间产生,在开发层面就能提供像普通函数一般的协作方式。

因为解决了这个痛点,协程开始变得越来越受开发者欢迎。而协程通过编译器的帮助把线程相关的代码留在了编译期间产生,开发者可以通过操作协程就可以达到使用线程的目的,所以现在大家认为协程是一种轻量级的线程。

对于多线程的协作,或者说异步代码之间的协作并不是只有协程一家解决方案,在JS中,有promise,Java中有RxJava等等,他们都致力于解决异步编程的相关问题,希望能以编写同步代码的方式来写异步代码,目前来看,他们都做的很不错。

感谢各位的阅读,以上就是“怎么理解Kotlin协程”的内容了,经过本文的学习后,相信大家对怎么理解Kotlin协程这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!


文章名称:怎么理解Kotlin协程
URL标题:http://pcwzsj.com/article/pogidi.html