Skip to content

Commit f1404c0

Browse files
authored
Remove mentions of obsolete actors from our quickstart guide (#3786)
Fixes #3785
1 parent 7b867fe commit f1404c0

File tree

2 files changed

+0
-135
lines changed

2 files changed

+0
-135
lines changed

docs/topics/shared-mutable-state-and-concurrency.md

Lines changed: 0 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -370,133 +370,11 @@ The locking in this example is fine-grained, so it pays the price. However, it i
370370
where you absolutely must modify some shared state periodically, but there is no natural thread that this state
371371
is confined to.
372372

373-
## Actors
374-
375-
An [actor](https://en.wikipedia.org/wiki/Actor_model) is an entity made up of a combination of a coroutine,
376-
the state that is confined and encapsulated into this coroutine,
377-
and a channel to communicate with other coroutines. A simple actor can be written as a function,
378-
but an actor with a complex state is better suited for a class.
379-
380-
There is an [actor] coroutine builder that conveniently combines actor's mailbox channel into its
381-
scope to receive messages from and combines the send channel into the resulting job object, so that a
382-
single reference to the actor can be carried around as its handle.
383-
384-
The first step of using an actor is to define a class of messages that an actor is going to process.
385-
Kotlin's [sealed classes](https://kotlinlang.org/docs/reference/sealed-classes.html) are well suited for that purpose.
386-
We define `CounterMsg` sealed class with `IncCounter` message to increment a counter and `GetCounter` message
387-
to get its value. The latter needs to send a response. A [CompletableDeferred] communication
388-
primitive, that represents a single value that will be known (communicated) in the future,
389-
is used here for that purpose.
390-
391-
```kotlin
392-
// Message types for counterActor
393-
sealed class CounterMsg
394-
object IncCounter : CounterMsg() // one-way message to increment counter
395-
class GetCounter(val response: CompletableDeferred<Int>) : CounterMsg() // a request with reply
396-
```
397-
398-
Then we define a function that launches an actor using an [actor] coroutine builder:
399-
400-
```kotlin
401-
// This function launches a new counter actor
402-
fun CoroutineScope.counterActor() = actor<CounterMsg> {
403-
var counter = 0 // actor state
404-
for (msg in channel) { // iterate over incoming messages
405-
when (msg) {
406-
is IncCounter -> counter++
407-
is GetCounter -> msg.response.complete(counter)
408-
}
409-
}
410-
}
411-
```
412-
413-
The main code is straightforward:
414-
415-
<!--- CLEAR -->
416-
417-
```kotlin
418-
import kotlinx.coroutines.*
419-
import kotlinx.coroutines.channels.*
420-
import kotlin.system.*
421-
422-
suspend fun massiveRun(action: suspend () -> Unit) {
423-
val n = 100 // number of coroutines to launch
424-
val k = 1000 // times an action is repeated by each coroutine
425-
val time = measureTimeMillis {
426-
coroutineScope { // scope for coroutines
427-
repeat(n) {
428-
launch {
429-
repeat(k) { action() }
430-
}
431-
}
432-
}
433-
}
434-
println("Completed ${n * k} actions in $time ms")
435-
}
436-
437-
// Message types for counterActor
438-
sealed class CounterMsg
439-
object IncCounter : CounterMsg() // one-way message to increment counter
440-
class GetCounter(val response: CompletableDeferred<Int>) : CounterMsg() // a request with reply
441-
442-
// This function launches a new counter actor
443-
fun CoroutineScope.counterActor() = actor<CounterMsg> {
444-
var counter = 0 // actor state
445-
for (msg in channel) { // iterate over incoming messages
446-
when (msg) {
447-
is IncCounter -> counter++
448-
is GetCounter -> msg.response.complete(counter)
449-
}
450-
}
451-
}
452-
453-
//sampleStart
454-
fun main() = runBlocking<Unit> {
455-
val counter = counterActor() // create the actor
456-
withContext(Dispatchers.Default) {
457-
massiveRun {
458-
counter.send(IncCounter)
459-
}
460-
}
461-
// send a message to get a counter value from an actor
462-
val response = CompletableDeferred<Int>()
463-
counter.send(GetCounter(response))
464-
println("Counter = ${response.await()}")
465-
counter.close() // shutdown the actor
466-
}
467-
//sampleEnd
468-
```
469-
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
470-
471-
> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt).
472-
>
473-
{type="note"}
474-
475-
<!--- TEST ARBITRARY_TIME
476-
Completed 100000 actions in xxx ms
477-
Counter = 100000
478-
-->
479-
480-
It does not matter (for correctness) what context the actor itself is executed in. An actor is
481-
a coroutine and a coroutine is executed sequentially, so confinement of the state to the specific coroutine
482-
works as a solution to the problem of shared mutable state. Indeed, actors may modify their own private state,
483-
but can only affect each other through messages (avoiding the need for any locks).
484-
485-
Actor is more efficient than locking under load, because in this case it always has work to do and it does not
486-
have to switch to a different context at all.
487-
488-
> Note that an [actor] coroutine builder is a dual of [produce] coroutine builder. An actor is associated
489-
> with the channel that it receives messages from, while a producer is associated with the channel that it
490-
> sends elements to.
491-
>
492-
{type="note"}
493-
494373
<!--- MODULE kotlinx-coroutines-core -->
495374
<!--- INDEX kotlinx.coroutines -->
496375

497376
[Dispatchers.Default]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
498377
[withContext]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html
499-
[CompletableDeferred]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-completable-deferred/index.html
500378

501379
<!--- INDEX kotlinx.coroutines.sync -->
502380

@@ -505,9 +383,4 @@ have to switch to a different context at all.
505383
[Mutex.unlock]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/unlock.html
506384
[withLock]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/with-lock.html
507385

508-
<!--- INDEX kotlinx.coroutines.channels -->
509-
510-
[actor]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html
511-
[produce]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html
512-
513386
<!--- END -->

kotlinx-coroutines-core/jvm/test/guide/test/SharedStateGuideTest.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,4 @@ class SharedStateGuideTest {
5656
"Counter = 100000"
5757
)
5858
}
59-
60-
@Test
61-
fun testExampleSync07() {
62-
test("ExampleSync07") { kotlinx.coroutines.guide.exampleSync07.main() }.verifyLinesArbitraryTime(
63-
"Completed 100000 actions in xxx ms",
64-
"Counter = 100000"
65-
)
66-
}
6759
}

0 commit comments

Comments
 (0)