blob: 753086160379f2cf8b898377325b435fe31c6a8d [file] [log] [blame] [view]
hadihariri7db55532018-09-15 10:35:08 +02001<!--- INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
2/*
3 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
4 */
5
6// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
Roman Elizarov0950dfa2018-07-13 10:33:25 +03007package kotlinx.coroutines.guide.$$1$$2
hadihariri7db55532018-09-15 10:35:08 +02008-->
9<!--- KNIT ../core/kotlinx-coroutines-core/test/guide/.*\.kt -->
10<!--- TEST_OUT ../core/kotlinx-coroutines-core/test/guide/test/SharedStateGuideTest.kt
11// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
Roman Elizarov0950dfa2018-07-13 10:33:25 +030012package kotlinx.coroutines.guide.test
hadihariri7db55532018-09-15 10:35:08 +020013
14import org.junit.Test
15
16class SharedStateGuideTest {
17-->
18## Table of contents
19
20<!--- TOC -->
21
22* [Shared mutable state and concurrency](#shared-mutable-state-and-concurrency)
23 * [The problem](#the-problem)
24 * [Volatiles are of no help](#volatiles-are-of-no-help)
25 * [Thread-safe data structures](#thread-safe-data-structures)
26 * [Thread confinement fine-grained](#thread-confinement-fine-grained)
27 * [Thread confinement coarse-grained](#thread-confinement-coarse-grained)
28 * [Mutual exclusion](#mutual-exclusion)
29 * [Actors](#actors)
30
31<!--- END_TOC -->
32
33## Shared mutable state and concurrency
34
35Coroutines can be executed concurrently using a multi-threaded dispatcher like the [Dispatchers.Default]. It presents
36all the usual concurrency problems. The main problem being synchronization of access to **shared mutable state**.
37Some solutions to this problem in the land of coroutines are similar to the solutions in the multi-threaded world,
38but others are unique.
39
40### The problem
41
42Let us launch a hundred coroutines all doing the same action thousand times.
43We'll also measure their completion time for further comparisons:
44
Robert GolusiƄskic33ef612018-10-23 11:29:56 +020045<div class="sample" markdown="1" theme="idea" data-highlight-only>
Alexander Prendotacbeef102018-09-27 18:42:04 +030046
hadihariri7db55532018-09-15 10:35:08 +020047```kotlin
48suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
49 val n = 100 // number of coroutines to launch
50 val k = 1000 // times an action is repeated by each coroutine
51 val time = measureTimeMillis {
52 val jobs = List(n) {
53 launch {
54 repeat(k) { action() }
55 }
56 }
57 jobs.forEach { it.join() }
58 }
59 println("Completed ${n * k} actions in $time ms")
60}
61```
62
Alexander Prendotacbeef102018-09-27 18:42:04 +030063</div>
64
hadihariri7db55532018-09-15 10:35:08 +020065We start with a very simple action that increments a shared mutable variable using
66multi-threaded [Dispatchers.Default] that is used in [GlobalScope].
67
Prendota0eee3c32018-10-22 12:52:56 +030068<!--- CLEAR -->
69
70<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +030071
hadihariri7db55532018-09-15 10:35:08 +020072```kotlin
Prendota0eee3c32018-10-22 12:52:56 +030073import kotlinx.coroutines.*
74import kotlin.system.*
75
76suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
77 val n = 100 // number of coroutines to launch
78 val k = 1000 // times an action is repeated by each coroutine
79 val time = measureTimeMillis {
80 val jobs = List(n) {
81 launch {
82 repeat(k) { action() }
83 }
84 }
85 jobs.forEach { it.join() }
86 }
87 println("Completed ${n * k} actions in $time ms")
88}
89
hadihariri7db55532018-09-15 10:35:08 +020090var counter = 0
91
Prendota65e6c8c2018-10-17 11:51:08 +030092fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +030093//sampleStart
hadihariri7db55532018-09-15 10:35:08 +020094 GlobalScope.massiveRun {
95 counter++
96 }
97 println("Counter = $counter")
Prendota0eee3c32018-10-22 12:52:56 +030098//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +020099}
100```
101
Alexander Prendotacbeef102018-09-27 18:42:04 +0300102</div>
103
hadihariri7db55532018-09-15 10:35:08 +0200104> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-01.kt)
105
106<!--- TEST LINES_START
107Completed 100000 actions in
108Counter =
109-->
110
111What does it print at the end? It is highly unlikely to ever print "Counter = 100000", because a thousand coroutines
112increment the `counter` concurrently from multiple threads without any synchronization.
113
114> Note: if you have an old system with 2 or fewer CPUs, then you _will_ consistently see 100000, because
115the thread pool is running in only one thread in this case. To reproduce the problem you'll need to make the
116following change:
117
Prendota0eee3c32018-10-22 12:52:56 +0300118<!--- CLEAR -->
119
120<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300121
hadihariri7db55532018-09-15 10:35:08 +0200122```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300123import kotlinx.coroutines.*
124import kotlin.system.*
125
126suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
127 val n = 100 // number of coroutines to launch
128 val k = 1000 // times an action is repeated by each coroutine
129 val time = measureTimeMillis {
130 val jobs = List(n) {
131 launch {
132 repeat(k) { action() }
133 }
134 }
135 jobs.forEach { it.join() }
136 }
137 println("Completed ${n * k} actions in $time ms")
138}
139
hadihariri7db55532018-09-15 10:35:08 +0200140val mtContext = newFixedThreadPoolContext(2, "mtPool") // explicitly define context with two threads
141var counter = 0
142
Prendota65e6c8c2018-10-17 11:51:08 +0300143fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300144//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200145 CoroutineScope(mtContext).massiveRun { // use it instead of Dispatchers.Default in this sample and below
146 counter++
147 }
148 println("Counter = $counter")
Prendota0eee3c32018-10-22 12:52:56 +0300149//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200150}
151```
152
Alexander Prendotacbeef102018-09-27 18:42:04 +0300153</div>
154
hadihariri7db55532018-09-15 10:35:08 +0200155> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-01b.kt)
156
157<!--- TEST LINES_START
158Completed 100000 actions in
159Counter =
160-->
161
162### Volatiles are of no help
163
164There is common misconception that making a variable `volatile` solves concurrency problem. Let us try it:
165
Prendota0eee3c32018-10-22 12:52:56 +0300166<!--- CLEAR -->
167
168<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300169
hadihariri7db55532018-09-15 10:35:08 +0200170```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300171import kotlinx.coroutines.*
172import kotlin.system.*
173
174suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
175 val n = 100 // number of coroutines to launch
176 val k = 1000 // times an action is repeated by each coroutine
177 val time = measureTimeMillis {
178 val jobs = List(n) {
179 launch {
180 repeat(k) { action() }
181 }
182 }
183 jobs.forEach { it.join() }
184 }
185 println("Completed ${n * k} actions in $time ms")
186}
187
hadihariri7db55532018-09-15 10:35:08 +0200188@Volatile // in Kotlin `volatile` is an annotation
189var counter = 0
190
Prendota65e6c8c2018-10-17 11:51:08 +0300191fun main() = runBlocking<Unit> {
hadihariri7db55532018-09-15 10:35:08 +0200192 GlobalScope.massiveRun {
193 counter++
194 }
195 println("Counter = $counter")
196}
197```
198
Alexander Prendotacbeef102018-09-27 18:42:04 +0300199</div>
200
hadihariri7db55532018-09-15 10:35:08 +0200201> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-02.kt)
202
203<!--- TEST LINES_START
204Completed 100000 actions in
205Counter =
206-->
207
208This code works slower, but we still don't get "Counter = 100000" at the end, because volatile variables guarantee
209linearizable (this is a technical term for "atomic") reads and writes to the corresponding variable, but
210do not provide atomicity of larger actions (increment in our case).
211
212### Thread-safe data structures
213
214The general solution that works both for threads and for coroutines is to use a thread-safe (aka synchronized,
215linearizable, or atomic) data structure that provides all the necessarily synchronization for the corresponding
216operations that needs to be performed on a shared state.
217In the case of a simple counter we can use `AtomicInteger` class which has atomic `incrementAndGet` operations:
218
Prendota0eee3c32018-10-22 12:52:56 +0300219<!--- CLEAR -->
220
221<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300222
hadihariri7db55532018-09-15 10:35:08 +0200223```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300224import kotlinx.coroutines.*
225import java.util.concurrent.atomic.*
226import kotlin.system.*
227
228suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
229 val n = 100 // number of coroutines to launch
230 val k = 1000 // times an action is repeated by each coroutine
231 val time = measureTimeMillis {
232 val jobs = List(n) {
233 launch {
234 repeat(k) { action() }
235 }
236 }
237 jobs.forEach { it.join() }
238 }
239 println("Completed ${n * k} actions in $time ms")
240}
241
hadihariri7db55532018-09-15 10:35:08 +0200242var counter = AtomicInteger()
243
Prendota65e6c8c2018-10-17 11:51:08 +0300244fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300245//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200246 GlobalScope.massiveRun {
247 counter.incrementAndGet()
248 }
249 println("Counter = ${counter.get()}")
Prendota0eee3c32018-10-22 12:52:56 +0300250//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200251}
252```
253
Alexander Prendotacbeef102018-09-27 18:42:04 +0300254</div>
255
hadihariri7db55532018-09-15 10:35:08 +0200256> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-03.kt)
257
258<!--- TEST ARBITRARY_TIME
259Completed 100000 actions in xxx ms
260Counter = 100000
261-->
262
263This is the fastest solution for this particular problem. It works for plain counters, collections, queues and other
264standard data structures and basic operations on them. However, it does not easily scale to complex
265state or to complex operations that do not have ready-to-use thread-safe implementations.
266
267### Thread confinement fine-grained
268
269_Thread confinement_ is an approach to the problem of shared mutable state where all access to the particular shared
270state is confined to a single thread. It is typically used in UI applications, where all UI state is confined to
271the single event-dispatch/application thread. It is easy to apply with coroutines by using a
272single-threaded context.
273
Prendota0eee3c32018-10-22 12:52:56 +0300274<!--- CLEAR -->
275
276<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300277
hadihariri7db55532018-09-15 10:35:08 +0200278```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300279import kotlinx.coroutines.*
280import kotlin.system.*
281
282suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
283 val n = 100 // number of coroutines to launch
284 val k = 1000 // times an action is repeated by each coroutine
285 val time = measureTimeMillis {
286 val jobs = List(n) {
287 launch {
288 repeat(k) { action() }
289 }
290 }
291 jobs.forEach { it.join() }
292 }
293 println("Completed ${n * k} actions in $time ms")
294}
295
hadihariri7db55532018-09-15 10:35:08 +0200296val counterContext = newSingleThreadContext("CounterContext")
297var counter = 0
298
Prendota65e6c8c2018-10-17 11:51:08 +0300299fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300300//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200301 GlobalScope.massiveRun { // run each coroutine with DefaultDispathcer
302 withContext(counterContext) { // but confine each increment to the single-threaded context
303 counter++
304 }
305 }
306 println("Counter = $counter")
Prendota0eee3c32018-10-22 12:52:56 +0300307//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200308}
309```
310
Alexander Prendotacbeef102018-09-27 18:42:04 +0300311</div>
312
hadihariri7db55532018-09-15 10:35:08 +0200313> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-04.kt)
314
315<!--- TEST ARBITRARY_TIME
316Completed 100000 actions in xxx ms
317Counter = 100000
318-->
319
320This code works very slowly, because it does _fine-grained_ thread-confinement. Each individual increment switches
321from multi-threaded [Dispatchers.Default] context to the single-threaded context using [withContext] block.
322
323### Thread confinement coarse-grained
324
325In practice, thread confinement is performed in large chunks, e.g. big pieces of state-updating business logic
326are confined to the single thread. The following example does it like that, running each coroutine in
327the single-threaded context to start with.
328Here we use [CoroutineScope()] function to convert coroutine context reference to [CoroutineScope]:
329
Prendota0eee3c32018-10-22 12:52:56 +0300330<!--- CLEAR -->
331
332<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300333
hadihariri7db55532018-09-15 10:35:08 +0200334```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300335import kotlinx.coroutines.*
336import kotlin.system.*
337
338suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
339 val n = 100 // number of coroutines to launch
340 val k = 1000 // times an action is repeated by each coroutine
341 val time = measureTimeMillis {
342 val jobs = List(n) {
343 launch {
344 repeat(k) { action() }
345 }
346 }
347 jobs.forEach { it.join() }
348 }
349 println("Completed ${n * k} actions in $time ms")
350}
351
hadihariri7db55532018-09-15 10:35:08 +0200352val counterContext = newSingleThreadContext("CounterContext")
353var counter = 0
354
Prendota65e6c8c2018-10-17 11:51:08 +0300355fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300356//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200357 CoroutineScope(counterContext).massiveRun { // run each coroutine in the single-threaded context
358 counter++
359 }
360 println("Counter = $counter")
Prendota0eee3c32018-10-22 12:52:56 +0300361//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200362}
363```
364
Alexander Prendotacbeef102018-09-27 18:42:04 +0300365</div>
366
hadihariri7db55532018-09-15 10:35:08 +0200367> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-05.kt)
368
369<!--- TEST ARBITRARY_TIME
370Completed 100000 actions in xxx ms
371Counter = 100000
372-->
373
374This now works much faster and produces correct result.
375
376### Mutual exclusion
377
378Mutual exclusion solution to the problem is to protect all modifications of the shared state with a _critical section_
379that is never executed concurrently. In a blocking world you'd typically use `synchronized` or `ReentrantLock` for that.
380Coroutine's alternative is called [Mutex]. It has [lock][Mutex.lock] and [unlock][Mutex.unlock] functions to
381delimit a critical section. The key difference is that `Mutex.lock()` is a suspending function. It does not block a thread.
382
383There is also [withLock] extension function that conveniently represents
384`mutex.lock(); try { ... } finally { mutex.unlock() }` pattern:
385
Prendota0eee3c32018-10-22 12:52:56 +0300386<!--- CLEAR -->
387
388<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300389
hadihariri7db55532018-09-15 10:35:08 +0200390```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300391import kotlinx.coroutines.*
392import kotlinx.coroutines.sync.*
393import kotlin.system.*
394
395suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
396 val n = 100 // number of coroutines to launch
397 val k = 1000 // times an action is repeated by each coroutine
398 val time = measureTimeMillis {
399 val jobs = List(n) {
400 launch {
401 repeat(k) { action() }
402 }
403 }
404 jobs.forEach { it.join() }
405 }
406 println("Completed ${n * k} actions in $time ms")
407}
408
hadihariri7db55532018-09-15 10:35:08 +0200409val mutex = Mutex()
410var counter = 0
411
Prendota65e6c8c2018-10-17 11:51:08 +0300412fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300413//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200414 GlobalScope.massiveRun {
415 mutex.withLock {
416 counter++
417 }
418 }
419 println("Counter = $counter")
Prendota0eee3c32018-10-22 12:52:56 +0300420//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200421}
422```
423
Alexander Prendotacbeef102018-09-27 18:42:04 +0300424</div>
425
hadihariri7db55532018-09-15 10:35:08 +0200426> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-06.kt)
427
428<!--- TEST ARBITRARY_TIME
429Completed 100000 actions in xxx ms
430Counter = 100000
431-->
432
433The locking in this example is fine-grained, so it pays the price. However, it is a good choice for some situations
434where you absolutely must modify some shared state periodically, but there is no natural thread that this state
435is confined to.
436
437### Actors
438
439An [actor](https://en.wikipedia.org/wiki/Actor_model) is an entity made up of a combination of a coroutine, the state that is confined and encapsulated into this coroutine,
440and a channel to communicate with other coroutines. A simple actor can be written as a function,
441but an actor with a complex state is better suited for a class.
442
443There is an [actor] coroutine builder that conveniently combines actor's mailbox channel into its
444scope to receive messages from and combines the send channel into the resulting job object, so that a
445single reference to the actor can be carried around as its handle.
446
447The first step of using an actor is to define a class of messages that an actor is going to process.
448Kotlin's [sealed classes](https://kotlinlang.org/docs/reference/sealed-classes.html) are well suited for that purpose.
449We define `CounterMsg` sealed class with `IncCounter` message to increment a counter and `GetCounter` message
450to get its value. The later needs to send a response. A [CompletableDeferred] communication
451primitive, that represents a single value that will be known (communicated) in the future,
452is used here for that purpose.
453
Alexander Prendotacbeef102018-09-27 18:42:04 +0300454<div class="sample" markdown="1" theme="idea" data-highlight-only>
455
hadihariri7db55532018-09-15 10:35:08 +0200456```kotlin
457// Message types for counterActor
458sealed class CounterMsg
459object IncCounter : CounterMsg() // one-way message to increment counter
460class GetCounter(val response: CompletableDeferred<Int>) : CounterMsg() // a request with reply
461```
462
Alexander Prendotacbeef102018-09-27 18:42:04 +0300463</div>
464
hadihariri7db55532018-09-15 10:35:08 +0200465Then we define a function that launches an actor using an [actor] coroutine builder:
466
Alexander Prendotacbeef102018-09-27 18:42:04 +0300467<div class="sample" markdown="1" theme="idea" data-highlight-only>
468
hadihariri7db55532018-09-15 10:35:08 +0200469```kotlin
470// This function launches a new counter actor
471fun CoroutineScope.counterActor() = actor<CounterMsg> {
472 var counter = 0 // actor state
473 for (msg in channel) { // iterate over incoming messages
474 when (msg) {
475 is IncCounter -> counter++
476 is GetCounter -> msg.response.complete(counter)
477 }
478 }
479}
480```
481
Alexander Prendotacbeef102018-09-27 18:42:04 +0300482</div>
483
hadihariri7db55532018-09-15 10:35:08 +0200484The main code is straightforward:
485
Prendota0eee3c32018-10-22 12:52:56 +0300486<!--- CLEAR -->
487
488<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300489
hadihariri7db55532018-09-15 10:35:08 +0200490```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300491import kotlinx.coroutines.*
492import kotlinx.coroutines.channels.*
493import kotlin.system.*
494
495suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
496 val n = 100 // number of coroutines to launch
497 val k = 1000 // times an action is repeated by each coroutine
498 val time = measureTimeMillis {
499 val jobs = List(n) {
500 launch {
501 repeat(k) { action() }
502 }
503 }
504 jobs.forEach { it.join() }
505 }
506 println("Completed ${n * k} actions in $time ms")
507}
508
509// Message types for counterActor
510sealed class CounterMsg
511object IncCounter : CounterMsg() // one-way message to increment counter
512class GetCounter(val response: CompletableDeferred<Int>) : CounterMsg() // a request with reply
513
514// This function launches a new counter actor
515fun CoroutineScope.counterActor() = actor<CounterMsg> {
516 var counter = 0 // actor state
517 for (msg in channel) { // iterate over incoming messages
518 when (msg) {
519 is IncCounter -> counter++
520 is GetCounter -> msg.response.complete(counter)
521 }
522 }
523}
524
Prendota65e6c8c2018-10-17 11:51:08 +0300525fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300526//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200527 val counter = counterActor() // create the actor
528 GlobalScope.massiveRun {
529 counter.send(IncCounter)
530 }
531 // send a message to get a counter value from an actor
532 val response = CompletableDeferred<Int>()
533 counter.send(GetCounter(response))
534 println("Counter = ${response.await()}")
535 counter.close() // shutdown the actor
Prendota0eee3c32018-10-22 12:52:56 +0300536//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200537}
538```
539
Alexander Prendotacbeef102018-09-27 18:42:04 +0300540</div>
541
hadihariri7db55532018-09-15 10:35:08 +0200542> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-07.kt)
543
544<!--- TEST ARBITRARY_TIME
545Completed 100000 actions in xxx ms
546Counter = 100000
547-->
548
549It does not matter (for correctness) what context the actor itself is executed in. An actor is
550a coroutine and a coroutine is executed sequentially, so confinement of the state to the specific coroutine
551works as a solution to the problem of shared mutable state. Indeed, actors may modify their own private state, but can only affect each other through messages (avoiding the need for any locks).
552
553Actor is more efficient than locking under load, because in this case it always has work to do and it does not
554have to switch to a different context at all.
555
556> Note, that an [actor] coroutine builder is a dual of [produce] coroutine builder. An actor is associated
557 with the channel that it receives messages from, while a producer is associated with the channel that it
558 sends elements to.
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300559
560<!--- MODULE kotlinx-coroutines-core -->
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300561<!--- INDEX kotlinx.coroutines -->
562[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
563[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
564[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html
565[CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html
566[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
567[CompletableDeferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-completable-deferred/index.html
568<!--- INDEX kotlinx.coroutines.sync -->
569[Mutex]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/index.html
570[Mutex.lock]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/lock.html
571[Mutex.unlock]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/unlock.html
572[withLock]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/with-lock.html
573<!--- INDEX kotlinx.coroutines.channels -->
574[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html
575[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300576<!--- END -->