@@ -57,24 +57,24 @@ They are launched with [launch] _coroutine builder_ in a context of some [Corout
57
57
Here we are launching a new coroutine in the [ GlobalScope] , meaning that the lifetime of the new
58
58
coroutine is limited only by the lifetime of the whole application.
59
59
60
- You can achieve the same result replacing
61
- ` GlobalScope.launch { ... } ` with ` thread { ... } ` and ` delay(...) ` with ` Thread.sleep(...) ` .
60
+ You can achieve the same result by replacing
61
+ ` GlobalScope.launch { ... } ` with ` thread { ... } ` , and ` delay(...) ` with ` Thread.sleep(...) ` .
62
62
Try it (don't forget to import ` kotlin.concurrent.thread ` ).
63
63
64
- If you start by replacing ` GlobalScope.launch ` by ` thread ` , the compiler produces the following error:
64
+ If you start by replacing ` GlobalScope.launch ` with ` thread ` , the compiler produces the following error:
65
65
66
66
```
67
67
Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function
68
68
```
69
69
70
- That is because [ delay] is a special _ suspending function_ that does not block a thread, but _ suspends_
71
- coroutine and it can be only used from a coroutine.
70
+ That is because [ delay] is a special _ suspending function_ that does not block a thread, but _ suspends_ the
71
+ coroutine, and it can be only used from a coroutine.
72
72
73
73
### Bridging blocking and non-blocking worlds
74
74
75
75
The first example mixes _ non-blocking_ ` delay(...) ` and _ blocking_ ` Thread.sleep(...) ` in the same code.
76
76
It is easy to lose track of which one is blocking and which one is not.
77
- Let's be explicit about blocking using [ runBlocking] coroutine builder:
77
+ Let's be explicit about blocking using the [ runBlocking] coroutine builder:
78
78
79
79
<div class =" sample " markdown =" 1 " theme =" idea " data-min-compiler-version =" 1.3 " >
80
80
@@ -195,15 +195,15 @@ the background job in any way. Much better.
195
195
There is still something to be desired for practical usage of coroutines.
196
196
When we use ` GlobalScope.launch ` , we create a top-level coroutine. Even though it is light-weight, it still
197
197
consumes some memory resources while it runs. If we forget to keep a reference to the newly launched
198
- coroutine it still runs. What if the code in the coroutine hangs (for example, we erroneously
198
+ coroutine, it still runs. What if the code in the coroutine hangs (for example, we erroneously
199
199
delay for too long), what if we launched too many coroutines and ran out of memory?
200
200
Having to manually keep references to all the launched coroutines and [ join] [ Job.join ] them is error-prone.
201
201
202
202
There is a better solution. We can use structured concurrency in our code.
203
203
Instead of launching coroutines in the [ GlobalScope] , just like we usually do with threads (threads are always global),
204
204
we can launch coroutines in the specific scope of the operation we are performing.
205
205
206
- In our example, we have ` main ` function that is turned into a coroutine using [ runBlocking] coroutine builder.
206
+ In our example, we have a ` main ` function that is turned into a coroutine using the [ runBlocking] coroutine builder.
207
207
Every coroutine builder, including ` runBlocking ` , adds an instance of [ CoroutineScope] to the scope of its code block.
208
208
We can launch coroutines in this scope without having to ` join ` them explicitly, because
209
209
an outer coroutine (` runBlocking ` in our example) does not complete until all the coroutines launched
@@ -233,12 +233,12 @@ World!
233
233
-->
234
234
235
235
### Scope builder
236
- In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using
237
- [ coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children
238
- complete.
239
236
240
- [ runBlocking] and [ coroutineScope] may look similar because they both wait for its body and all its children to complete.
241
- The main difference between these two is that the [ runBlocking] method _ blocks_ the current thread for waiting,
237
+ In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the
238
+ [ coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete.
239
+
240
+ [ runBlocking] and [ coroutineScope] may look similar because they both wait for their body and all its children to complete.
241
+ The main difference is that the [ runBlocking] method _ blocks_ the current thread for waiting,
242
242
while [ coroutineScope] just suspends, releasing the underlying thread for other usages.
243
243
Because of that difference, [ runBlocking] is a regular function and [ coroutineScope] is a suspending function.
244
244
@@ -280,16 +280,16 @@ Task from nested launch
280
280
Coroutine scope is over
281
281
-->
282
282
283
- Note that right after "Task from coroutine scope" message, while waiting for nested launch,
284
- "Task from runBlocking" is executed and printed, though coroutineScope is not completed yet.
283
+ Note that right after the "Task from coroutine scope" message ( while waiting for nested launch)
284
+ "Task from runBlocking" is executed and printed — even though the [ coroutineScope] is not completed yet.
285
285
286
286
### Extract function refactoring
287
287
288
288
Let's extract the block of code inside ` launch { ... } ` into a separate function. When you
289
- perform "Extract function" refactoring on this code you get a new function with ` suspend ` modifier.
290
- That is your first _ suspending function_ . Suspending functions can be used inside coroutines
289
+ perform "Extract function" refactoring on this code, you get a new function with the ` suspend ` modifier.
290
+ This is your first _ suspending function_ . Suspending functions can be used inside coroutines
291
291
just like regular functions, but their additional feature is that they can, in turn,
292
- use other suspending functions, like ` delay ` in this example, to _ suspend_ execution of a coroutine.
292
+ use other suspending functions ( like ` delay ` in this example) to _ suspend_ execution of a coroutine.
293
293
294
294
<div class =" sample " markdown =" 1 " theme =" idea " data-min-compiler-version =" 1.3 " >
295
295
@@ -319,11 +319,11 @@ World!
319
319
320
320
321
321
But what if the extracted function contains a coroutine builder which is invoked on the current scope?
322
- In this case ` suspend ` modifier on the extracted function is not enough. Making ` doWorld ` an extension
323
- method on ` CoroutineScope ` is one of the solutions, but it may not always be applicable as it does not make API clearer.
322
+ In this case, the ` suspend ` modifier on the extracted function is not enough. Making ` doWorld ` an extension
323
+ method on ` CoroutineScope ` is one of the solutions, but it may not always be applicable as it does not make the API clearer.
324
324
The idiomatic solution is to have either an explicit ` CoroutineScope ` as a field in a class containing the target function
325
325
or an implicit one when the outer class implements ` CoroutineScope ` .
326
- As a last resort, [ CoroutineScope(coroutineContext)] [ CoroutineScope() ] can be used, but such approach is structurally unsafe
326
+ As a last resort, [ CoroutineScope(coroutineContext)] [ CoroutineScope() ] can be used, but such an approach is structurally unsafe
327
327
because you no longer have control on the scope of execution of this method. Only private APIs can use this builder.
328
328
329
329
### Coroutines ARE light-weight
@@ -352,6 +352,7 @@ fun main() = runBlocking {
352
352
<!-- - TEST lines.size == 1 && lines[0] == ".".repeat(100_000) -->
353
353
354
354
It launches 100K coroutines and, after a second, each coroutine prints a dot.
355
+
355
356
Now, try that with threads. What would happen? (Most likely your code will produce some sort of out-of-memory error)
356
357
357
358
### Global coroutines are like daemon threads
0 commit comments