kotlin协程
Kotlin comes up with coroutines that help us writing asynchronous code in a synchronous manner. Android is a single thread platform. By default, everything runs on Main Thread (UI Thread) so when its time to run non-UI related operations (E.g. Network call, DB operation, File I/O operations or any time taking task), We dedicate these tasks to different threads and on completion if needed, pass back the result to UI Thread.
Kotlin提供了协程,可以帮助我们以同步方式编写异步代码。 Android是单线程平台。 默认情况下,所有内容都在主线程(UI线程)上运行,因此当其时间运行与非UI相关的操作(例如网络调用,数据库操作,文件I / O操作或任何需要时间的任务)时,我们会将这些任务专用于不同的线程并在需要时完成,将结果传回UI线程。
Android has its own mechanisms to perform a task in another thread such as Async task, Handler, Services, etc. These all are nice and work pretty well when used diligently. These mechanisms include callbacks, post methods, etc. for passing the result among threads but wouldn’t it be nicer if we can get rid of all these callbacks and can write async code the same way we write sync code.
Android具有自己的机制来在另一个线程中执行任务,例如异步任务,处理程序,服务等。这些都很好,并且在认真使用时也能很好地工作。 这些机制包括用于在线程之间传递结果的回调,发布方法等,但是如果我们能够摆脱所有这些回调并以与编写同步代码相同的方式编写异步代码,那就更好了。
// For example, something like this in Main Thread.
val response = Async_operation() //async operation
if (response.isSuccessful()) doThis() else doThat()
Yes, That’s how easy your code can look like if you use coroutine. We do not need to put a callback, Next line will get executed once the response comes. You would think that calling it from main thread will block it by the time response comes. Well, If you use coroutine, that would not be the case. It won’t block Main thread or any thread as a matter of fact and can still execute code in sync manner. Well, let’s try to figure out the missing part.
是的,如果您使用协程,这就是您的代码看起来容易的地方。 我们不需要放置回调,一旦响应到来,下一行将被执行。 您可能会认为,从主线程调用它会在响应出现时阻止它。 好吧,如果您使用协程,那就不是这种情况。 实际上,它不会阻塞主线程或任何线程,并且仍可以同步方式执行代码。 好吧,让我们尝试找出缺失的部分。
让我们从比较协程和线程开始 (Let’s start by comparing coroutine with thread)
In the example above, let’s say we create a new thread and once the task is completed, we pass back the result to UI thread. We can do that right? Yes, for sure but there are few problems with this approach. Let me list down some of them.
在上面的示例中,假设我们创建了一个新线程,一旦任务完成,我们会将结果传递回UI线程。 我们可以做到吗? 是的,可以肯定,但是这种方法几乎没有问题。 让我列出其中一些。
1. Passing data from one thread to another is a headache. Also, it does not look very clean. Most of the time we end up using callbacks or some sort of notify mechanism.2. Threads are expensive. Creating and stopping them is expensive, involves creating own stack. Threads are managed by OS. Thread scheduler adds extra overhead to schedule the threads.3. Threads are blocking. If you are performing a task as simple as delaying the execution for a second (Sleep), Thread would be blocked and can not be used for any other operation.4. Threads are not lifecycle aware. They do not have any knowledge of Lifecycle components (Activity, Fragment, ViewModel). A thread will be running even if UI component is destroyed which requires us to handle clean up and memory leaks.
1.将数据从一个线程传递到另一个线程是一件令人头疼的事情。 另外,它看起来也不是很干净。 大多数时候,我们最终使用回调或某种通知机制。2。 线程很昂贵 。 创建和停止它们很昂贵,需要创建自己的堆栈。 线程由OS管理。 线程调度程序会增加额外的开销来调度线程3。 线程正在阻塞 。 如果您执行的任务只是延迟执行一秒钟(Sleep),那么Thread将被阻塞,不能用于其他任何操作。4。 线程不了解生命周期 。 他们对生命周期组件(活动,片段,视图模型)不了解。 即使UI组件被销毁,线程也将运行,这需要我们处理清理和内存泄漏。
Apart from this, how would your code look like with lots of Threads, Async tasks, etc? We might end up with lots of callbacks, lifecycle handling methods, passing data from one place to another which makes the reading difficult. Altogether, we would end up worrying and writing more about handling all these rather than the actual logic.
除此之外,带有大量线程,异步任务等的代码看起来如何? 我们可能最终会遇到许多回调,生命周期处理方法,将数据从一个地方传递到另一个地方,这使读取变得困难。 总而言之,我们最终将担心并写更多关于处理所有这些问题的知识,而不是实际的逻辑。
So let me say it loud “It’s not just another way of Async programming, it’s completely different paradigm of async programming”. Even if you are not convinced, I would recommend you to try it once. What do we know, it might become your go-to man in this Asynchronous world.
因此,我要大声地说:“ 这不仅是异步编程的另一种方式,它是异步编程的完全不同的范例 ”。 即使您不相信,我也建议您尝试一次。 我们知道,它可能成为您在这个异步世界中的首选。
Okay, Enough of chanting. Let’s understand the deal.
好吧,足够高呼。 让我们了解这笔交易。
协程轻巧超快 (Coroutines are light and super fast)
Let’s not trust what we have to say to each other and let the code do the talking.
让我们不信任彼此之间要说的话,让代码进行交谈。
I am going to create 10k Threads. I know, You are wondering why you would ever do that in the real world. Well, let’s do this to understand the impact coroutine can create.
我将创建10k线程。 我知道,您想知道为什么在现实世界中会这么做。 好吧,让我们这样做来了解协程可以产生的影响。
fun creating_10k_Thread() {
val time = measureTimeMillis {
for(i in 1..10000) {
Thread(Runnable {
Thread.sleep(1)
}).run()
}
}
}
Here, each thread is sleeping for 1 ms. Running this function took around 12.6 seconds. Now let’s Create 100k Coroutines (10 times more), and increase the delay to 10 seconds (10000 times higher). Don’t worry about ‘runBlocking’ or ‘launch’ (Coroutine Builders) for now.
在这里,每个线程Hibernate1 ms。 运行此功能大约花费了12.6秒。 现在,我们创建100k协程(增加10倍),并将延迟增加到10秒(提高10000倍)。 现在不用担心“ runBlocking”或“ launch”(协程生成器)。
fun creatingCoroutines(){
val time = measureTimeMillis {
runBlocking {
for(i in 1..100000) {
launch {
delay(10000L)
}
}
}
}
}
Voila! 14 seconds. The delay itself is 10 seconds. This is how lightweight it is. Not to mention, if I try to create 100k threads, It might take forever.
瞧! 14秒。 延迟本身是10秒。 这是多么轻巧。 更不用说,如果我尝试创建100k线程,那可能要花很多时间。
Now we all agree that Coroutines are super fast compared to Threads but how does coroutine do that.
现在我们都同意协程比Threads超级快,但是协程如何做到这一点。
If you look at the creating_10k_Thread() method, you see there is a delay of 1ms. Well, during this delay thread is blocked. In other words, it can not do anything else in that 1ms and will continue after that 1ms. Also, you can create only a certain number of threads depending upon system cores. Let’s say you can create up to 8 threads within your system. Looking at the example, we are running a loop till 10000 counts. 1st 8 times, 8 threads will be created and would run in parallel. On the 9th iteration, another thread can not be created unless there is an available thread. Since we have put sleep for 1ms for a thread, it would be blocked for that much time and would not be able to perform any other operation. Once the delay is over, next thread will be created. To sum up, threads are being blocked for 1ms which is causing the delay, Total blocking time for the method would be 10000/
如果查看creating_10k_Thread()方法,则会看到延迟为1毫秒。 好吧,在此延迟期间,线程被阻塞。 换句话说,它在那1ms内不能做任何其他事情,并且会在那1ms之后继续。 另外,您只能根据系统核心创建一定数量的线程。 假设您可以在系统中最多创建8个线程。 查看示例,我们正在运行一个循环,直到10000个计数为止。 1次8次,将创建8个线程并并行运行。 在第9次迭代中,除非有可用线程,否则无法创建另一个线程。 由于我们为线程设置了1ms的睡眠时间,因此它将被阻塞这么长时间,并且将无法执行任何其他操作。 一旦延迟结束,将创建下一个线程。 总结起来,线程被阻塞了1ms,这导致了延迟, 该方法的总阻塞时间将是10000 / <最大线程数> ms。 此外,线程调度程序将管理您的线程,这会增加额外的开销。
In case of creatingCoroutines() method, we have put a delay of 10 sec. The thing about coroutine is that it does not block, it suspends. So while it is waiting for 10 seconds delay to be completed it can pick up any other task and resume once the delay is over. Also, coroutines are user-managed, OS does not have a say in it. which makes it even faster. To put it in numbers, each thread has its own stack, typically 1MB in size. 64k is the least amount of stack space allowed per thread in the JVM while a simple coroutine in Kotlin occupies only a few dozen bytes of heap memory.
如果使用createdCoroutines()方法,我们将延迟时间设置为10秒。 关于协程的事情是它不会阻塞,而是会暂停。 因此,在等待10秒延迟完成时,它可以接管其他任何任务,并在延迟结束后继续执行。 另外,协程是由用户管理的,操作系统中没有发言权。 这使得它甚至更快。 用数字表示,每个线程都有自己的堆栈,大小通常为1MB。 64k是JVM中每个线程允许的最小堆栈空间量,而Kotlin中的一个简单协程仅占用几十个字节的堆内存。
Just to be sure that we understand the difference in suspend and block clearly. I am adding up one more example. If you are clear with the differences, jump to the next section.
只是为了确保我们清楚了解暂停和阻止的区别。 我要再举一个例子。 如果您清楚差异,请跳至下一部分。
In snippet 1, We are calling fun1 and fun2 methods sequentially on the main thread. Execution will have a delay of 1 second during which thread would be blocked and would not be able to perform any other task. Now let’s try to write same code using coroutine.
在代码段1中,我们在主线程上依次调用fun1和fun2方法。 执行将有1秒的延迟,在此期间线程将被阻塞并且无法执行任何其他任务。 现在,让我们尝试使用协程编写相同的代码。
In snippet 2, It looks like they are running in parallel but that’s not possible since both of them are getting executed by a single thread. Both functions are running concurrently and that’s because delay function does not block the thread, it suspends the thread so now without wasting time same thread can start performing next task and can return to it once the other suspended function (delay) returns to it.
在代码段2中,看起来它们正在并行运行,但这是不可能的,因为它们都是由单个线程执行的。 这两个函数同时运行,这是因为delay函数不会阻塞线程,它会挂起线程,因此现在不会浪费时间,同一线程可以开始执行下一个任务,并且一旦另一个挂起的函数(延迟)返回到它,就可以返回到它。
A coroutine can provide a very high level of concurrency with very small overhead. Multiple threads can also provide parallelism but there is blocking and context switching. Coroutine suspends the thread and does not block it so that it can switch to another work. With a lot of coroutines doing very small bits of work and voluntarily switching between each other, It can provide efficiency and faster execution which can never be achieved from a scheduler which is why you can have thousands of coroutines working together as opposed to tens of threads. Let’s move to the next question.
协程可以以很小的开销提供非常高的并发性。 多个线程也可以提供并行性,但是存在阻塞和上下文切换。 协程暂停线程并且不阻塞它,以便它可以切换到其他工作。 由于许多协程仅需很少的工作并在彼此之间进行自动切换,因此它可以提供效率和更快的执行速度,而这是调度程序无法实现的,这就是为什么您可以让成千上万的协程一起工作而不是使用数十个线程的原因。 让我们转到下一个问题。
协程如何暂停自身? (How does coroutine suspend itself?)
private fun main() {
launch(Dispatchers.Default) {
asyncOperation() // this is running in bg thread
launch(Dispatchers.Main) {
completionHandler() // this is running in main thread.
}
}
}private suspend fun asyncOperation() {
log("Thread is ${Thread.currentThread().name} AsyncOperation")
log("Started async operation")
delay(3000)
log("Completed async operation")
}private fun completionHandler() {
log("Thread is ${Thread.currentThread().name} completionHandler")
log("Running after async opearion")
label.text = "Async operation completed" //ui thread
}Output:
Thread is DefaultDispatcher-worker-1 AsyncOperation
Started async operation
Completed async operation
Thread is main completionHandler
Running after async opearion
If you look at the output, you would find that ‘completionHandler’ is executed after ‘asyncOperation’ is completed. ‘asyncOperation’ is running in a background thread and ‘completionHandler’ waiting for it to be completed. In ‘completionHandler’, a textview is being updated. Let’s look at the byte code of ‘asyncOperation’ method.
如果查看输出,则会发现在完成“ asyncOperation”之后执行了“ completionHandler”。 “ asyncOperation”正在后台线程中运行,“ completionHandler”正在等待其完成。 在“ completionHandler”中,正在更新文本视图。 让我们看一下“ asyncOperation”方法的字节码。
Check the 2nd line from the top, there is a new parameter called ‘continuation’ added to asyncOperation method. Continuation is the real deal, this takes care of code suspension. Continuation gets added as a param to the function if it has ‘suspend’ modifier to it. Continuation stores the current state of the program. You can think of it like passing rest of the code (in this case completionHandler() method) inside Continuation wrapper. Once the current task is completed, continuation block will be executed. So every time you are creating a suspend function, you are adding continuation param in it which is wrapping rest of the code from same coroutine.
从顶部检查第二行,在asyncOperation方法中添加了一个名为“ continuation”的新参数。 延续才是真正的任务,这需要代码暂停。 如果函数具有“挂起”修饰符,则将其作为参数添加到函数中。 Continuation存储程序的当前状态。 您可以将其视为在Continuation包装器中传递其余代码(在本例中为completeHandler()方法)。 当前任务完成后,将执行继续块。 因此,每次创建暂停函数时,都在其中添加了延续参数,该参数封装了来自同一协程的其余代码。
If you are here, I hope you have found a reason to use coroutine. What we have seen is a small part of, what coroutine has to offer. Additionally, Coroutine works really well with Livedata, Room, Retrofit, etc. So I will encourage you to start using coroutine in your projects. Only if you try it, you would know the power of it. I will pause here and leave you with one example.
如果您在这里,希望您找到使用协程的理由。 我们所看到的只是协程所提供的一小部分。 此外,Coroutine在Livedata,Room,Retrofit等方面确实能很好地工作。因此,我鼓励您开始在项目中使用协程。 只有尝试一下,您才能知道它的强大功能。 我将在这里暂停,并举例说明。
In the example, I have created a BaseActivity class and a MainActivity class which is inheriting BaseActivity. In MainActivity.asyncWait, We are performing 2 Async operations and logging the result once both are completed. (Its not complete project, this contains only code related to coroutine)
在示例中,我创建了一个BaseActivity类和一个继承BaseActivity的MainActivity类。 在MainActivity.asyncWait中,我们正在执行2个Async操作,并在两个操作完成后记录结果。 (它不是完整的项目,仅包含与协程相关的代码)
I recommend you to read through the example thoroughly and try to understand the terminologies on your own. You might want to start with Dispatchers and Coroutine builders.
我建议您通读示例,并尝试自己理解术语。 您可能想从Dispatchers和Coroutine构建器开始。
Lastly, thank you for reading the article! Any questions and suggestions are most welcome. See you soon.
最后,感谢您阅读本文! 任何问题和建议都欢迎。 再见。
翻译自: https://medium.com/android-microsoft/kotlin-coroutines-1c8e009cb711
kotlin协程