Skip to content

Commit 43e9011

Browse files
committed
Note on systems with nCPUs <= 2
1 parent 52e1500 commit 43e9011

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

coroutines-guide.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!--- INCLUDE .*/example-([a-z]+)-([0-9]+)\.kt
1+
<!--- INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
22
/*
33
* Copyright 2016-2017 JetBrains s.r.o.
44
*
@@ -1549,7 +1549,7 @@ but others are unique.
15491549
Let us launch a thousand coroutines all doing the same action thousand times (for a total of a million executions).
15501550
We'll also measure their completion time for further comparisons:
15511551

1552-
<!--- INCLUDE .*/example-sync-([0-9]+).kt
1552+
<!--- INCLUDE .*/example-sync-([0-9a-z]+).kt
15531553
import kotlin.coroutines.experimental.CoroutineContext
15541554
import kotlin.system.measureTimeMillis
15551555
-->
@@ -1582,7 +1582,7 @@ suspend fun massiveRun(context: CoroutineContext, action: suspend () -> Unit) {
15821582
}
15831583
```
15841584

1585-
<!--- INCLUDE .*/example-sync-([0-9]+).kt -->
1585+
<!--- INCLUDE .*/example-sync-([0-9a-z]+).kt -->
15861586

15871587
We start with a very simple action that increments a shared mutable variable using
15881588
multi-threaded [CommonPool] context.
@@ -1608,6 +1608,29 @@ Counter =
16081608
What does it print at the end? It is highly unlikely to ever print "Counter = 1000000", because a thousand coroutines
16091609
increment the `counter` concurrently from multiple threads without any synchronization.
16101610

1611+
> Note: if you have an old system with 2 or fewer CPUs, then you _will_ consistently see 1000000, because
1612+
`CommonPool` is running in only one thread in this case. To reproduce the problem you'll need to make the
1613+
following change:
1614+
1615+
```kotlin
1616+
val mtContext = newFixedThreadPoolContext(2, "mtPool") // explicitly define context with two threads
1617+
var counter = 0
1618+
1619+
fun main(args: Array<String>) = runBlocking<Unit> {
1620+
massiveRun(mtContext) { // use it instead of CommonPool in this sample and below
1621+
counter++
1622+
}
1623+
println("Counter = $counter")
1624+
}
1625+
```
1626+
1627+
> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/guide/example-sync-01b.kt)
1628+
1629+
<!--- TEST LINES_START
1630+
Completed 1000000 actions in
1631+
Counter =
1632+
-->
1633+
16111634
### Volatiles are of no help
16121635

16131636
There is common misconception that making a variable `volatile` solves concurrency problem. Let us try it:
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2016-2017 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
18+
package guide.sync.example01b
19+
20+
import kotlinx.coroutines.experimental.*
21+
import kotlin.coroutines.experimental.CoroutineContext
22+
import kotlin.system.measureTimeMillis
23+
24+
suspend fun massiveRun(context: CoroutineContext, action: suspend () -> Unit) {
25+
val n = 1000 // number of coroutines to launch
26+
val k = 1000 // times an action is repeated by each coroutine
27+
val time = measureTimeMillis {
28+
val jobs = List(n) {
29+
launch(context) {
30+
repeat(k) { action() }
31+
}
32+
}
33+
jobs.forEach { it.join() }
34+
}
35+
println("Completed ${n * k} actions in $time ms")
36+
}
37+
38+
val mtContext = newFixedThreadPoolContext(2, "mtPool") // explicitly define context with two threads
39+
var counter = 0
40+
41+
fun main(args: Array<String>) = runBlocking<Unit> {
42+
massiveRun(mtContext) { // use it instead of CommonPool in this sample and below
43+
counter++
44+
}
45+
println("Counter = $counter")
46+
}

kotlinx-coroutines-core/src/test/kotlin/guide/test/GuideTest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,14 @@ class GuideTest {
353353
)
354354
}
355355

356+
@Test
357+
fun testGuideSyncExample01b() {
358+
test { guide.sync.example01b.main(emptyArray()) }.verifyLinesStart(
359+
"Completed 1000000 actions in",
360+
"Counter ="
361+
)
362+
}
363+
356364
@Test
357365
fun testGuideSyncExample02() {
358366
test { guide.sync.example02.main(emptyArray()) }.verifyLinesStart(

0 commit comments

Comments
 (0)