Kotlin 是一门仅在标准库中提供最基本底层 API 以便各种其他库能够利用协程的语言。与许多其他具有类似功能的语言不同,async 与 await 在 Kotlin 中并不是关键字,甚至都不是标准库的一部分。此外,Kotlin 的 挂起函数 概念为异步操作提供了比 future 与 promise 更安全、更不易出错的抽象。
kotlinx.coroutines 是由 JetBrains 开发的功能丰富的协程库。它包含本指南中涵盖的很多启用高级协程的原语,包括 launch、 async 等等。
如需了解其他kotlin用法,可查看如下:
Android Kotlin实战之高阶使用泛型扩展协程懒加载详解_蜗牛、Z的博客-CSDN博客
Android kotlin在实战过程问题总结与开发技巧详解_kotlin 同步锁_蜗牛、Z的博客-CSDN博客
Kotlin语法详解与实践教程,区分JAVA以及如何闭坑_kotlin mapof可以嵌套 to_蜗牛、Z的博客-CSDN博客
suspend的字面含义是暂停、挂起的意思。在kotlin中,代码执行到 suspend 函数的时候会『挂起』,并且这个『挂起』是非阻塞式的,它不会阻塞你当前的线程,挂起的定位: 暂时切走,稍后再切回来,和java的Thread.sleep()不一样,一个是阻塞,一个是等待,类似wait。
阻塞的基本原理一直占用CUP,不让出如果sleep,挂起的是将自己的cup时间让出去,等待重新分配。这样就很好理解了。用最直观的理解是一种异步操作
如果当前方法被suspend修饰了,如果不是通过runblock或者coroutines 库调用,那么调用链上的所有函数都要背suspend修饰
suspend fun main() {
delay(1000)
println("hello")
}
又因为我们在业务中,很难会获取到main方法,所以无法通过main方法在源头解决协程的调用
runBlocking是阻塞调用,如果通过它来处理协程,那么只有当runBlocking的方法体中执行完,才会往下执行,会一直霸占cpu,直到运行完才会释放。
fun main() {
runBlocking {
delay(1000)
println("hello---block")
}
println("hello")
}

kotlinx.coroutines 异步库的调用是先挂起线程代码,继续执行,等下面执行完才会又回到协程函数执行
fun main() {
GlobalScope.launch { // 在后台启动一个新的协程并继续
println("suspend start!") // 在延迟后打印输出
delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
println("suspend end!") // 在延迟后打印输出
}
println("sleep start,") // 协程已在等待时主线程还在继续
Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
println("sleep end,") // 协程已在等待时主线程还在继续
}

通过以上日志分析,协程提都不会被执行,被挂起了,先执行了下面的代码,遇到sleep函数,形成阻塞,协程又获取到cpu的时间,开始执行协程代码快,执行完,sleep阻塞完成又继续执行。
coroutines 库的使用kotlinx.coroutines是填补了kotlin在Java中的缺失,也提供了异步处理协程的问题。
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"
GlobalScope.launch { // 在后台启动一个新的协程并继续
//协程代码调用处
}
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
}
直接调用delay,设置需要阻塞延迟的时间
launch会返回一个对象Job,job就是当前的任务,如果你接受了这个Job,那么代码块将不会执行,需要你手动调用
fun main() {
val job = GlobalScope.launch { // 启动一个新协程并保持对这个作业的引用
delay(1000L)
println("World!")
}
println("Hello,")
runBlocking {
job.join() // 等待直到子协程执行结束
}
}
job的join只能在阻塞方法中调用,或者suspend方法体,不能在
GlobalScope.launch {
job.join()
}
调用,否则不会执行当前代码块。因为launch已返回了一个job,当前job没有执行,所以,改job一直处于一个未执行状态。
也可以取消:job.cancel()
协程作用域:launch{}
八股文显示,前者比后者快,通过源码分析,GlobalScope是继承了CoroutineScope,是对CoroutineScope的封装,提供一个静态函数。
协程的实际使用还有一些需要改进的地方。 当我们使用 GlobalScope.launch 时,我们会创建一个顶层协程。虽然它很轻量,但它运行时仍会消耗一些内存资源。有一个更好的解决办法。我们可以在代码中使用结构化并发。 我们可以在执行操作所在的指定作用域内启动协程, 而不是像通常使用线程(线程总是全局的)那样在 GlobalScope 中启动.
fun main() = runBlocking { // this: CoroutineScope
launch { // 在 runBlocking 作用域中启动一个新协程
delay(1000L)
println("World!")
}
println("Hello,")
}
除了由不同的构建器提供协程作用域之外,还可以使用 coroutineScope 构建器声明自己的作用域。它会创建一个协程作用域并且在所有已启动子协程执行完毕之前不会结束.
runBlocking 与 coroutineScope 可能看起来很类似,因为它们都会等待其协程体以及所有子协程结束。 主要区别在于,runBlocking 方法会阻塞当前线程来等待, 而 coroutineScope 只是挂起,会释放底层线程用于其他用途。 由于存在这点差异,runBlocking 是常规函数,而 coroutineScope 是挂起函数
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking 1")
}
coroutineScope { // 创建一个协程作用域
launch {
delay(500L)
println("Task from nested launch 2")
}
delay(100L)
println("Task from coroutine scope 3") // 这一行会在内嵌 launch 之前输出
}
println("Coroutine scope is over 4") // 这一行在内嵌 launch 执行完毕后才输出
}

GlobalScope的async异步挂起,支持返回值,且不能单独直接使用,需在runBlocking体内执行。
fun main()= runBlocking {
println("current thread 1")
//launch启动后台调度线程,并且不堵塞当前线程
val deferred = GlobalScope.async {
println("async thread 2=")
delay(1000)
println("async end 3")
//需要通过标签的方式返回
return@async "123"
}
println("current thread end 4")
val result = deferred.await()
println("result 5= $result")
//当前线程休眠以便调度线程有机会执行
Thread.sleep(3000)
}
协程上下文包含一个 协程调度器 (参见 CoroutineDispatcher)它确定了相关的协程在哪个线程或哪些线程上执行。协程调度器可以将协程限制在一个特定的线程执行,或将它分派到一个线程池,亦或是让它不受限地运行。
launch { // 运行在父协程的上下文中,即 runBlocking 主协程
println("main runBlocking : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Unconfined) { // 不受限的——将工作在主线程中
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Default) { // 将会获取默认调度器
println("Default : I'm working in thread ${Thread.currentThread().name}")
}
launch(newSingleThreadContext("MyOwnThread")) { // 将使它获得一个新的线程
println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
}
public actual object Dispatchers {
@JvmStatic
public actual val Default: CoroutineDispatcher = DefaultScheduler
@JvmStatic
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
@JvmStatic
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
@JvmStatic
public val IO: CoroutineDispatcher = DefaultIoScheduler
@DelicateCoroutinesApi
public fun shutdown() {
DefaultExecutor.shutdown()
// Also shuts down Dispatchers.IO
DefaultScheduler.shutdown()
}
}
协程可以在一个线程上挂起并在其它线程上恢复。 如果没有特殊工具,甚至对于一个单线程的调度器也是难以弄清楚协程在何时何地正在做什么事情。
| runBlocking | 创建新的协程,运行在当前线程上,所以会堵塞当前线程,直到协程体结束 |
| GlobalScope.launch | 启动一个新的线程,在新线程上创建运行协程,不堵塞当前线程 |
| GlobalScope.asyn | 启动一个新的线程,在新线程上创建运行协程,并且不堵塞当前线程,支持 通过await获取返回值 |
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po