@@ -246,9 +246,15 @@ fun main() {
246
246
>
247
247
{style="note"}
248
248
249
- It demonstrates several new techniques. One is using [ runBlocking] with an explicitly specified context, and
250
- the other one is using the [ withContext] function to change the context of a coroutine while still staying in the
251
- same coroutine, as you can see in the output below:
249
+ The example above demonstrates new techniques in coroutine usage.
250
+
251
+ The first technique shows how to use [ runBlocking] with a specified context.
252
+ The second technique involves calling [ withContext] ,
253
+ which may suspend the current coroutine and switch to a new context—provided the new context differs from the existing one.
254
+ Specifically, if you specify a different [ CoroutineDispatcher] , extra dispatches are required:
255
+ the block is scheduled on the new dispatcher, and once it finishes, execution returns to the original dispatcher.
256
+
257
+ As a result, the output of the above code is:
252
258
253
259
``` text
254
260
[Ctx1 @coroutine#1] Started in ctx1
@@ -258,8 +264,8 @@ same coroutine, as you can see in the output below:
258
264
259
265
<!-- - TEST -->
260
266
261
- Note that this example also uses the ` use ` function from the Kotlin standard library to release threads
262
- created with [ newSingleThreadContext] when they are no longer needed.
267
+ The example above uses the ` use ` function from the Kotlin standard library
268
+ to properly release thread resources created by [ newSingleThreadContext] when they're no longer needed.
263
269
264
270
## Job in the context
265
271
@@ -281,7 +287,7 @@ fun main() = runBlocking<Unit> {
281
287
>
282
288
{style="note"}
283
289
284
- In the [ debug mode] ( #debugging-coroutines-and-threads ) , it outputs something like this:
290
+ In [ debug mode] ( #debugging-coroutines-and-threads ) , it outputs something like this:
285
291
286
292
```
287
293
My job is "coroutine#1":BlockingCoroutine{Active}@6d311334
@@ -300,12 +306,12 @@ the [Job] of the new coroutine becomes
300
306
a _ child_ of the parent coroutine's job. When the parent coroutine is cancelled, all its children
301
307
are recursively cancelled, too.
302
308
303
- However, this parent-child relation can be explicitly overriden in one of two ways:
309
+ However, this parent-child relation can be explicitly overridden in one of two ways:
304
310
305
311
1 . When a different scope is explicitly specified when launching a coroutine (for example, ` GlobalScope.launch ` ),
306
- then it does not inherit a ` Job ` from the parent scope.
307
- 2 . When a different ` Job ` object is passed as the context for the new coroutine (as shown in the example below),
308
- then it overrides the ` Job ` of the parent scope.
312
+ it does not inherit a ` Job ` from the parent scope.
313
+ 2 . When a different ` Job ` object is passed as the context for the new coroutine (as shown in the example below),
314
+ it overrides the ` Job ` of the parent scope.
309
315
310
316
In both cases, the launched coroutine is not tied to the scope it was launched from and operates independently.
311
317
@@ -356,7 +362,8 @@ job1: I am not affected by cancellation of the request
356
362
357
363
## Parental responsibilities
358
364
359
- A parent coroutine always waits for completion of all its children. A parent does not have to explicitly track
365
+ A parent coroutine always waits for the completion of all its children.
366
+ A parent does not have to explicitly track
360
367
all the children it launches, and it does not have to use [ Job.join] to wait for them at the end:
361
368
362
369
``` kotlin
@@ -480,11 +487,15 @@ I'm working in thread DefaultDispatcher-worker-1 @test#2
480
487
481
488
## Coroutine scope
482
489
483
- Let us put our knowledge about contexts, children and jobs together. Assume that our application has
484
- an object with a lifecycle, but that object is not a coroutine. For example, we are writing an Android application
485
- and launch various coroutines in the context of an Android activity to perform asynchronous operations to fetch
486
- and update data, do animations, etc. All of these coroutines must be cancelled when the activity is destroyed
487
- to avoid memory leaks. We, of course, can manipulate contexts and jobs manually to tie the lifecycles of the activity
490
+ Let us put our knowledge about contexts, children, and jobs together.
491
+ Assume that our application has an object with a lifecycle, but that object is not a coroutine.
492
+ For example,
493
+ we are writing an Android application,
494
+ and launching various coroutines in the context of an Android activity
495
+ to perform asynchronous operations to fetch and update data,
496
+ do animations, etc. These coroutines must be cancelled when the activity is destroyed
497
+ to avoid memory leaks.
498
+ We, of course, can manipulate contexts and jobs manually to tie the lifecycles of the activity
488
499
and its coroutines, but ` kotlinx.coroutines ` provides an abstraction encapsulating that: [ CoroutineScope] .
489
500
You should be already familiar with the coroutine scope as all coroutine builders are declared as extensions on it.
490
501
@@ -521,8 +532,9 @@ For the demo, we launch ten coroutines that delay for a different time:
521
532
```
522
533
523
534
In our main function we create the activity, call our test ` doSomething ` function, and destroy the activity after 500ms.
524
- This cancels all the coroutines that were launched from ` doSomething ` . We can see that because after the destruction
525
- of the activity no more messages are printed, even if we wait a little longer.
535
+ This cancels all the coroutines that were launched from ` doSomething ` .
536
+ We can see that because after the destruction
537
+ of the activity, no more messages are printed, even if we wait a little longer.
526
538
527
539
<!-- - CLEAR -->
528
540
@@ -577,16 +589,16 @@ Destroying activity!
577
589
<!-- - TEST -->
578
590
579
591
As you can see, only the first two coroutines print a message and the others are cancelled
580
- by a single invocation of ` job .cancel()` in ` Activity.destroy() ` .
592
+ by a single invocation of [ ` mainScope .cancel()` ] [ CoroutineScope.cancel ] in ` Activity.destroy() ` .
581
593
582
- > Note, that Android has first-party support for coroutine scope in all entities with the lifecycle.
594
+ > Note that Android has first-party support for coroutine scope in all entities with the lifecycle.
583
595
> See [ the corresponding documentation] ( https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope ) .
584
596
>
585
597
{style="note"}
586
598
587
599
### Thread-local data
588
600
589
- Sometimes it is convenient to have an ability to pass some thread-local data to or between coroutines.
601
+ Sometimes it is convenient to be able to pass some thread-local data to or between coroutines.
590
602
However, since they are not bound to any particular thread, this will likely lead to boilerplate if done manually.
591
603
592
604
For [ ` ThreadLocal ` ] ( https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html ) ,
@@ -620,8 +632,8 @@ fun main() = runBlocking<Unit> {
620
632
>
621
633
{style="note"}
622
634
623
- In this example we launch a new coroutine in a background thread pool using [ Dispatchers.Default] , so
624
- it works on a different thread from the thread pool, but it still has the value of the thread local variable
635
+ In this example, we launch a new coroutine in a background thread pool using [ Dispatchers.Default] , so
636
+ it works on different threads from the thread pool, but it still has the value of the thread local variable
625
637
that we specified using ` threadLocal.asContextElement(value = "launch") ` ,
626
638
no matter which thread the coroutine is executed on.
627
639
Thus, the output (with [ debug] ( #debugging-coroutines-and-threads ) ) is:
@@ -636,7 +648,7 @@ Post-main, current thread: Thread[main @coroutine#1,5,main], thread local value:
636
648
<!-- - TEST FLEXIBLE_THREAD -->
637
649
638
650
It's easy to forget to set the corresponding context element. The thread-local variable accessed from the coroutine may
639
- then have an unexpected value, if the thread running the coroutine is different.
651
+ then have an unexpected value if the thread running the coroutine is different.
640
652
To avoid such situations, it is recommended to use the [ ensurePresent] method
641
653
and fail-fast on improper usages.
642
654
@@ -646,11 +658,12 @@ It has one key limitation, though: when a thread-local is mutated, a new value i
646
658
Use [ withContext] to update the value of the thread-local in a coroutine, see [ asContextElement] for more details.
647
659
648
660
Alternatively, a value can be stored in a mutable box like ` class Counter(var i: Int) ` , which is, in turn,
649
- stored in a thread-local variable. However, in this case you are fully responsible to synchronize
661
+ stored in a thread-local variable.
662
+ However, in this case, you are fully responsible to synchronize
650
663
potentially concurrent modifications to the variable in this mutable box.
651
664
652
- For advanced usage, for example for integration with logging MDC, transactional contexts or any other libraries
653
- which internally use thread-locals for passing data, see the documentation of the [ ThreadContextElement] interface
665
+ For advanced usage, for example, for integration with logging MDC, transactional contexts or any other libraries
666
+ that internally use thread-locals for passing data, see the documentation of the [ ThreadContextElement] interface
654
667
that should be implemented.
655
668
656
669
<!-- - MODULE kotlinx-coroutines-core -->
@@ -676,6 +689,7 @@ that should be implemented.
676
689
[ CoroutineScope() ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html
677
690
[ MainScope() ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html
678
691
[ Dispatchers.Main ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
692
+ [ CoroutineScope.cancel ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel.html
679
693
[ asContextElement ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/as-context-element.html
680
694
[ ensurePresent ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-present.html
681
695
[ ThreadContextElement ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html
0 commit comments