@@ -370,133 +370,11 @@ The locking in this example is fine-grained, so it pays the price. However, it i
370
370
where you absolutely must modify some shared state periodically, but there is no natural thread that this state
371
371
is confined to.
372
372
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
-
494
373
<!-- - MODULE kotlinx-coroutines-core -->
495
374
<!-- - INDEX kotlinx.coroutines -->
496
375
497
376
[ Dispatchers.Default ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
498
377
[ 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
500
378
501
379
<!-- - INDEX kotlinx.coroutines.sync -->
502
380
@@ -505,9 +383,4 @@ have to switch to a different context at all.
505
383
[ Mutex.unlock ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/unlock.html
506
384
[ withLock ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/with-lock.html
507
385
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
-
513
386
<!-- - END -->
0 commit comments