Skip to content

Commit d94652f

Browse files
committed
Update "Shared mutable state and concurrency" guide with best practices
* Properly use structured concurrency in the example code. * Also remove a special not on <= 2 core CPU, which is no longer needed because default dispatcher has always at least two CPUs. Fixed #1241
1 parent 86559c6 commit d94652f

File tree

10 files changed

+205
-244
lines changed

10 files changed

+205
-244
lines changed

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

Lines changed: 109 additions & 131 deletions
Large diffs are not rendered by default.

kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,30 @@ package kotlinx.coroutines.guide.sync01
88
import kotlinx.coroutines.*
99
import kotlin.system.*
1010

11-
suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
11+
suspend fun massiveRun(action: suspend () -> Unit) {
1212
val n = 100 // number of coroutines to launch
1313
val k = 1000 // times an action is repeated by each coroutine
1414
val time = measureTimeMillis {
15-
val jobs = List(n) {
16-
launch {
17-
repeat(k) { action() }
15+
coroutineScope { // scope for coroutines
16+
repeat(n) {
17+
launch {
18+
repeat(k) { action() }
19+
}
1820
}
1921
}
20-
jobs.forEach { it.join() }
2122
}
2223
println("Completed ${n * k} actions in $time ms")
2324
}
2425

26+
//sampleStart
2527
var counter = 0
2628

27-
fun main() = runBlocking<Unit> {
28-
//sampleStart
29-
GlobalScope.massiveRun {
30-
counter++
29+
fun main() = runBlocking {
30+
withContext(Dispatchers.Default) {
31+
massiveRun {
32+
counter++
33+
}
3134
}
3235
println("Counter = $counter")
33-
//sampleEnd
3436
}
37+
//sampleEnd

kotlinx-coroutines-core/jvm/test/guide/example-sync-01b.kt

Lines changed: 0 additions & 35 deletions
This file was deleted.

kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,31 @@ package kotlinx.coroutines.guide.sync02
88
import kotlinx.coroutines.*
99
import kotlin.system.*
1010

11-
suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
11+
suspend fun massiveRun(action: suspend () -> Unit) {
1212
val n = 100 // number of coroutines to launch
1313
val k = 1000 // times an action is repeated by each coroutine
1414
val time = measureTimeMillis {
15-
val jobs = List(n) {
16-
launch {
17-
repeat(k) { action() }
15+
coroutineScope { // scope for coroutines
16+
repeat(n) {
17+
launch {
18+
repeat(k) { action() }
19+
}
1820
}
1921
}
20-
jobs.forEach { it.join() }
2122
}
2223
println("Completed ${n * k} actions in $time ms")
2324
}
2425

26+
//sampleStart
2527
@Volatile // in Kotlin `volatile` is an annotation
2628
var counter = 0
2729

28-
fun main() = runBlocking<Unit> {
29-
GlobalScope.massiveRun {
30-
counter++
30+
fun main() = runBlocking {
31+
withContext(Dispatchers.Default) {
32+
massiveRun {
33+
counter++
34+
}
3135
}
3236
println("Counter = $counter")
3337
}
38+
//sampleEnd

kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,30 @@ import kotlinx.coroutines.*
99
import java.util.concurrent.atomic.*
1010
import kotlin.system.*
1111

12-
suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
12+
suspend fun massiveRun(action: suspend () -> Unit) {
1313
val n = 100 // number of coroutines to launch
1414
val k = 1000 // times an action is repeated by each coroutine
1515
val time = measureTimeMillis {
16-
val jobs = List(n) {
17-
launch {
18-
repeat(k) { action() }
16+
coroutineScope { // scope for coroutines
17+
repeat(n) {
18+
launch {
19+
repeat(k) { action() }
20+
}
1921
}
2022
}
21-
jobs.forEach { it.join() }
2223
}
2324
println("Completed ${n * k} actions in $time ms")
2425
}
2526

27+
//sampleStart
2628
var counter = AtomicInteger()
2729

28-
fun main() = runBlocking<Unit> {
29-
//sampleStart
30-
GlobalScope.massiveRun {
31-
counter.incrementAndGet()
30+
fun main() = runBlocking {
31+
withContext(Dispatchers.Default) {
32+
massiveRun {
33+
counter.incrementAndGet()
34+
}
3235
}
33-
println("Counter = ${counter.get()}")
34-
//sampleEnd
36+
println("Counter = $counter")
3537
}
38+
//sampleEnd

kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,34 @@ package kotlinx.coroutines.guide.sync04
88
import kotlinx.coroutines.*
99
import kotlin.system.*
1010

11-
suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
11+
suspend fun massiveRun(action: suspend () -> Unit) {
1212
val n = 100 // number of coroutines to launch
1313
val k = 1000 // times an action is repeated by each coroutine
1414
val time = measureTimeMillis {
15-
val jobs = List(n) {
16-
launch {
17-
repeat(k) { action() }
15+
coroutineScope { // scope for coroutines
16+
repeat(n) {
17+
launch {
18+
repeat(k) { action() }
19+
}
1820
}
1921
}
20-
jobs.forEach { it.join() }
2122
}
2223
println("Completed ${n * k} actions in $time ms")
2324
}
2425

26+
//sampleStart
2527
val counterContext = newSingleThreadContext("CounterContext")
2628
var counter = 0
2729

28-
fun main() = runBlocking<Unit> {
29-
//sampleStart
30-
GlobalScope.massiveRun { // run each coroutine with DefaultDispathcer
31-
withContext(counterContext) { // but confine each increment to the single-threaded context
32-
counter++
30+
fun main() = runBlocking {
31+
withContext(Dispatchers.Default) {
32+
massiveRun {
33+
// confine each increment to a single-threaded context
34+
withContext(counterContext) {
35+
counter++
36+
}
3337
}
3438
}
3539
println("Counter = $counter")
36-
//sampleEnd
3740
}
41+
//sampleEnd

kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,32 @@ package kotlinx.coroutines.guide.sync05
88
import kotlinx.coroutines.*
99
import kotlin.system.*
1010

11-
suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
11+
suspend fun massiveRun(action: suspend () -> Unit) {
1212
val n = 100 // number of coroutines to launch
1313
val k = 1000 // times an action is repeated by each coroutine
1414
val time = measureTimeMillis {
15-
val jobs = List(n) {
16-
launch {
17-
repeat(k) { action() }
15+
coroutineScope { // scope for coroutines
16+
repeat(n) {
17+
launch {
18+
repeat(k) { action() }
19+
}
1820
}
1921
}
20-
jobs.forEach { it.join() }
2122
}
2223
println("Completed ${n * k} actions in $time ms")
2324
}
2425

26+
//sampleStart
2527
val counterContext = newSingleThreadContext("CounterContext")
2628
var counter = 0
2729

28-
fun main() = runBlocking<Unit> {
29-
//sampleStart
30-
CoroutineScope(counterContext).massiveRun { // run each coroutine in the single-threaded context
31-
counter++
30+
fun main() = runBlocking {
31+
// confine everything to a single-threaded context
32+
withContext(counterContext) {
33+
massiveRun {
34+
counter++
35+
}
3236
}
3337
println("Counter = $counter")
34-
//sampleEnd
3538
}
39+
//sampleEnd

kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,34 @@ import kotlinx.coroutines.*
99
import kotlinx.coroutines.sync.*
1010
import kotlin.system.*
1111

12-
suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
12+
suspend fun massiveRun(action: suspend () -> Unit) {
1313
val n = 100 // number of coroutines to launch
1414
val k = 1000 // times an action is repeated by each coroutine
1515
val time = measureTimeMillis {
16-
val jobs = List(n) {
17-
launch {
18-
repeat(k) { action() }
16+
coroutineScope { // scope for coroutines
17+
repeat(n) {
18+
launch {
19+
repeat(k) { action() }
20+
}
1921
}
2022
}
21-
jobs.forEach { it.join() }
2223
}
2324
println("Completed ${n * k} actions in $time ms")
2425
}
2526

27+
//sampleStart
2628
val mutex = Mutex()
2729
var counter = 0
2830

29-
fun main() = runBlocking<Unit> {
30-
//sampleStart
31-
GlobalScope.massiveRun {
32-
mutex.withLock {
33-
counter++
31+
fun main() = runBlocking {
32+
withContext(Dispatchers.Default) {
33+
massiveRun {
34+
// protect each increment with lock
35+
mutex.withLock {
36+
counter++
37+
}
3438
}
3539
}
3640
println("Counter = $counter")
37-
//sampleEnd
3841
}
42+
//sampleEnd

kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ import kotlinx.coroutines.*
99
import kotlinx.coroutines.channels.*
1010
import kotlin.system.*
1111

12-
suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
12+
suspend fun massiveRun(action: suspend () -> Unit) {
1313
val n = 100 // number of coroutines to launch
1414
val k = 1000 // times an action is repeated by each coroutine
1515
val time = measureTimeMillis {
16-
val jobs = List(n) {
17-
launch {
18-
repeat(k) { action() }
16+
coroutineScope { // scope for coroutines
17+
repeat(n) {
18+
launch {
19+
repeat(k) { action() }
20+
}
1921
}
2022
}
21-
jobs.forEach { it.join() }
2223
}
2324
println("Completed ${n * k} actions in $time ms")
2425
}
@@ -39,16 +40,18 @@ fun CoroutineScope.counterActor() = actor<CounterMsg> {
3940
}
4041
}
4142

42-
fun main() = runBlocking<Unit> {
4343
//sampleStart
44+
fun main() = runBlocking {
4445
val counter = counterActor() // create the actor
45-
GlobalScope.massiveRun {
46-
counter.send(IncCounter)
46+
withContext(Dispatchers.Default) {
47+
massiveRun {
48+
counter.send(IncCounter)
49+
}
4750
}
4851
// send a message to get a counter value from an actor
4952
val response = CompletableDeferred<Int>()
5053
counter.send(GetCounter(response))
5154
println("Counter = ${response.await()}")
5255
counter.close() // shutdown the actor
53-
//sampleEnd
5456
}
57+
//sampleEnd

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@ class SharedStateGuideTest {
1313
)
1414
}
1515

16-
@Test
17-
fun testKotlinxCoroutinesGuideSync01b() {
18-
test("KotlinxCoroutinesGuideSync01b") { kotlinx.coroutines.guide.sync01b.main() }.verifyLinesStart(
19-
"Completed 100000 actions in",
20-
"Counter ="
21-
)
22-
}
23-
2416
@Test
2517
fun testKotlinxCoroutinesGuideSync02() {
2618
test("KotlinxCoroutinesGuideSync02") { kotlinx.coroutines.guide.sync02.main() }.verifyLinesStart(

0 commit comments

Comments
 (0)