1
- <!-- - INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
1
+ <!-- - INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
2
2
/*
3
3
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
4
4
*/
@@ -14,33 +14,29 @@ package kotlinx.coroutines.guide.test
14
14
import org.junit.Test
15
15
16
16
class SharedStateGuideTest {
17
- -->
18
- ## Table of contents
17
+ -->
18
+ ## 目录
19
19
20
20
<!-- - TOC -->
21
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 )
22
+ * [ 共享的可变状态和并发 ] ( #shared-mutable-state-and-concurrency )
23
+ * [ 问题 ] ( #the-problem )
24
+ * [ 没有发挥作用的volatile ] ( #volatiles-are-of-no-help )
25
+ * [ 线程安全的数据结构 ] ( #thread-safe-data-structures )
26
+ * [ 以细粒度限制线程 ] ( #thread-confinement-fine-grained )
27
+ * [ 以粗粒度限制线程 ] ( #thread-confinement-coarse-grained )
28
+ * [ 互斥 ] ( #mutual-exclusion )
29
29
* [ Actors] ( #actors )
30
30
31
31
<!-- - END_TOC -->
32
32
33
- ## Shared mutable state and concurrency
33
+ ## 共享的可变状态和并发
34
34
35
- Coroutines can be executed concurrently using a multi-threaded dispatcher like the [ Dispatchers.Default] . It presents
36
- all the usual concurrency problems. The main problem being synchronization of access to ** shared mutable state** .
37
- Some solutions to this problem in the land of coroutines are similar to the solutions in the multi-threaded world,
38
- but others are unique.
35
+ 协程可用多线程调度器(比如默认的 [ Dispatchers.Default] )并发执行。这样就可以提出所有常见的并发问题。主要的问题是同步访问** 共享的可变状态** 。协程领域对这个问题的一些解决方案类似于多线程领域中的解决方案,但其他解决方案则是独一无二的。
39
36
40
- ### The problem
37
+ ### 问题
41
38
42
- Let us launch a hundred coroutines all doing the same action thousand times.
43
- We'll also measure their completion time for further comparisons:
39
+ 我们启动一百个协程,它们做一千次相同的动作。我们同时会测量它们的完成时间,以便进一步的比较:
44
40
45
41
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
46
42
@@ -60,10 +56,9 @@ suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
60
56
}
61
57
```
62
58
63
- </div >
59
+ </div >
64
60
65
- We start with a very simple action that increments a shared mutable variable using
66
- multi-threaded [ Dispatchers.Default] that is used in [ GlobalScope] .
61
+ 我们从一个非常简单的动作开始:在 [ GlobalScope] 中使用多线程的 [ Dispatchers.Default] 递增一个共享的可变变量。
67
62
68
63
<!-- - CLEAR -->
69
64
@@ -101,19 +96,16 @@ fun main() = runBlocking<Unit> {
101
96
102
97
</div >
103
98
104
- > You can get full code [ here ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-01.kt )
99
+ > 你可以点击 [ 这里 ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-01.kt ) 获得完整代码
105
100
106
101
<!-- - TEST LINES_START
107
102
Completed 100000 actions in
108
103
Counter =
109
104
-->
110
105
111
- What does it print at the end? It is highly unlikely to ever print "Counter = 100000", because a thousand coroutines
112
- increment the ` counter ` concurrently from multiple threads without any synchronization.
106
+ 这段代码最后打印出什么结果?它不太可能打印出“Counter = 100000”,因为一千个协程在一百个线程中同时递增计数器而且没有做并发处理。
113
107
114
- > Note: if you have an old system with 2 or fewer CPUs, then you _ will_ consistently see 100000, because
115
- the thread pool is running in only one thread in this case. To reproduce the problem you'll need to make the
116
- following change:
108
+ > 注意:如果你的运行机器使用两个或者更少的cpu,那么你总是会看到 100000,因为线程池在这种情况下只会在一个线程中运行。要重现这个问题,可以做如下的变动:
117
109
118
110
<!-- - CLEAR -->
119
111
@@ -142,7 +134,7 @@ var counter = 0
142
134
143
135
fun main () = runBlocking<Unit > {
144
136
// sampleStart
145
- CoroutineScope (mtContext).massiveRun { // use it instead of Dispatchers.Default in this sample and below
137
+ CoroutineScope (mtContext).massiveRun { // use it instead of Dispatchers.Default in this sample and below
146
138
counter++
147
139
}
148
140
println (" Counter = $counter " )
@@ -152,16 +144,17 @@ fun main() = runBlocking<Unit> {
152
144
153
145
</div >
154
146
155
- > You can get full code [ here ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-01b.kt )
147
+ > 你可以点击 [ 这里 ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-01b.kt ) 获得完整代码
156
148
157
149
<!-- - TEST LINES_START
158
150
Completed 100000 actions in
159
151
Counter =
160
152
-->
161
153
162
- ### Volatiles are of no help
154
+ ### 没有发挥作用的volatile
155
+
156
+ 有一种常见的误解:volatile 可以解决并发问题。让我们尝试一下:
163
157
164
- There is common misconception that making a variable ` volatile ` solves concurrency problem. Let us try it:
165
158
166
159
<!-- - CLEAR -->
167
160
@@ -185,7 +178,7 @@ suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
185
178
println (" Completed ${n * k} actions in $time ms" )
186
179
}
187
180
188
- @Volatile // in Kotlin `volatile` is an annotation
181
+ @Volatile // in Kotlin `volatile` is an annotation
189
182
var counter = 0
190
183
191
184
fun main () = runBlocking<Unit > {
@@ -198,23 +191,19 @@ fun main() = runBlocking<Unit> {
198
191
199
192
</div >
200
193
201
- > You can get full code [ here ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-02.kt )
194
+ > 你可以点击 [ 这里 ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-02.kt ) 获得完整代码
202
195
203
196
<!-- - TEST LINES_START
204
197
Completed 100000 actions in
205
198
Counter =
206
199
-->
207
200
208
- This code works slower, but we still don't get "Counter = 100000" at the end, because volatile variables guarantee
209
- linearizable (this is a technical term for "atomic") reads and writes to the corresponding variable, but
210
- do not provide atomicity of larger actions (increment in our case).
201
+ 这段代码运行速度更慢了,但我们仍然没有得到 “Counter = 100000”,因为 volatile 变量保证可线性化(这是“原子”的技术术语)读取和写入变量,但在大量动作(在我们的示例中即“递增”操作)发生时并不提供原子性。
211
202
212
- ### Thread-safe data structures
213
203
214
- The general solution that works both for threads and for coroutines is to use a thread-safe (aka synchronized,
215
- linearizable, or atomic) data structure that provides all the necessarily synchronization for the corresponding
216
- operations that needs to be performed on a shared state.
217
- In the case of a simple counter we can use ` AtomicInteger ` class which has atomic ` incrementAndGet ` operations:
204
+ ### 线程安全的数据结构
205
+
206
+ 一种对线程、协程都有效的常规解决方法,就是使用线程安全(也称为同步的、可线性化、原子)的数据结构,它为需要在共享状态上执行的相应操作提供所有必需的同步处理。在简单的计数器场景中,我们可以使用具有 ` incrementAndGet ` 原子操作的 ` AtomicInteger ` 类:
218
207
219
208
<!-- - CLEAR -->
220
209
@@ -253,23 +242,19 @@ fun main() = runBlocking<Unit> {
253
242
254
243
</div >
255
244
256
- > You can get full code [ here ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-03.kt )
245
+ > 你可以点击 [ 这里 ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-03.kt ) 获得完整代码
257
246
258
247
<!-- - TEST ARBITRARY_TIME
259
248
Completed 100000 actions in xxx ms
260
249
Counter = 100000
261
250
-->
262
251
263
- This is the fastest solution for this particular problem. It works for plain counters, collections, queues and other
264
- standard data structures and basic operations on them. However, it does not easily scale to complex
265
- state or to complex operations that do not have ready-to-use thread-safe implementations.
252
+ 这是针对此类特定问题的最快解决方案。它适用于普通计数器、集合、队列和其他标准数据结构以及它们的基本操作。然而,它并不容易扩展为应对复杂状态、或复杂操作没有现成的线程安全实现的情况。
253
+
266
254
267
- ### Thread confinement fine-grained
255
+ ### 以细粒度限制线程
268
256
269
- _ Thread confinement_ is an approach to the problem of shared mutable state where all access to the particular shared
270
- state is confined to a single thread. It is typically used in UI applications, where all UI state is confined to
271
- the single event-dispatch/application thread. It is easy to apply with coroutines by using a
272
- single-threaded context.
257
+ _ 限制线程_ 是解决共享可变状态问题的一种方案,其中对特定共享状态的所有访问权都限制在单个线程中。它通常应用于UI程序中:所有UI状态都局限于单个事件分发线程或应用主线程中。这在协程中很容易实现,通过使用一个单线程上下文:
273
258
274
259
<!-- - CLEAR -->
275
260
@@ -310,22 +295,19 @@ fun main() = runBlocking<Unit> {
310
295
311
296
</div >
312
297
313
- > You can get full code [ here ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-04.kt )
298
+ > 你可以点击 [ 这里 ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-04.kt ) 获得完整代码
314
299
315
300
<!-- - TEST ARBITRARY_TIME
316
301
Completed 100000 actions in xxx ms
317
302
Counter = 100000
318
303
-->
319
304
320
- This code works very slowly, because it does _ fine-grained_ thread-confinement. Each individual increment switches
321
- from multi-threaded [ Dispatchers.Default] context to the single-threaded context using [ withContext] block.
305
+ 这段代码运行非常缓慢,因为它进行了 _ 细粒度_ 的线程限制。每个增量操作都得使用 [ withContext] 块从多线程 [ Dispatchers.Default] 上下文切换到单线程上下文。
322
306
323
- ### Thread confinement coarse-grained
307
+ ### 以粗粒度限制线程
308
+
309
+ 在实践中,线程限制是在大段代码中执行的,例如:状态更新类业务逻辑中大部分都是限于单线程中。下面的示例演示了这种情况,在单线程上下文中运行每个协程。这里我们使用 [ CoroutineScope()] 函数来切换协程上下文为 [ CoroutineScope] :
324
310
325
- In practice, thread confinement is performed in large chunks, e.g. big pieces of state-updating business logic
326
- are confined to the single thread. The following example does it like that, running each coroutine in
327
- the single-threaded context to start with.
328
- Here we use [ CoroutineScope()] function to convert coroutine context reference to [ CoroutineScope] :
329
311
330
312
<!-- - CLEAR -->
331
313
@@ -364,24 +346,20 @@ fun main() = runBlocking<Unit> {
364
346
365
347
</div >
366
348
367
- > You can get full code [ here ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-05.kt )
349
+ > 你可以点击 [ 这里 ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-05.kt ) 获得完整代码
368
350
369
351
<!-- - TEST ARBITRARY_TIME
370
352
Completed 100000 actions in xxx ms
371
353
Counter = 100000
372
354
-->
373
355
374
- This now works much faster and produces correct result.
356
+ 这段代码运行更快而且打印出了正确的结果。
375
357
376
- ### Mutual exclusion
358
+ ### 互斥
377
359
378
- Mutual exclusion solution to the problem is to protect all modifications of the shared state with a _ critical section_
379
- that is never executed concurrently. In a blocking world you'd typically use ` synchronized ` or ` ReentrantLock ` for that.
380
- Coroutine's alternative is called [ Mutex] . It has [ lock] [ Mutex.lock ] and [ unlock] [ Mutex.unlock ] functions to
381
- delimit a critical section. The key difference is that ` Mutex.lock() ` is a suspending function. It does not block a thread.
360
+ 该问题的互斥解决方案是使用永远不会同时执行的 _ 关键代码块_ 来保护共享状态的所有修改。在阻塞的世界中,你通常会为此目的使用 ` synchronized ` 或者 ` ReentrantLock ` 。在协程中的替代品叫做 [ Mutex] 。它具有 [ lock] [ Mutex.lock ] 和 [ unlock] [ Mutex.unlock ] 方法,可以隔离关键的部分。关键的区别在于 ` Mutex.lock() ` 是一个挂起函数,它不会阻塞线程。
382
361
383
- There is also [ withLock] extension function that conveniently represents
384
- ` mutex.lock(); try { ... } finally { mutex.unlock() } ` pattern:
362
+ 还有 [ withLock] 扩展函数,可以方便的替代常用的 ` mutex.lock(); try { ... } finally { mutex.unlock() } ` 模式:
385
363
386
364
<!-- - CLEAR -->
387
365
@@ -423,33 +401,23 @@ fun main() = runBlocking<Unit> {
423
401
424
402
</div >
425
403
426
- > You can get full code [ here ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-06.kt )
404
+ > 你可以点击 [ 这里 ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-06.kt ) 获得完整代码
427
405
428
406
<!-- - TEST ARBITRARY_TIME
429
407
Completed 100000 actions in xxx ms
430
408
Counter = 100000
431
409
-->
432
410
433
- The locking in this example is fine-grained, so it pays the price. However, it is a good choice for some situations
434
- where you absolutely must modify some shared state periodically, but there is no natural thread that this state
435
- is confined to.
411
+ 此示例中锁是细粒度的,因此会付出一些代价。但是对于某些必须定期修改共享状态的场景,它是一个不错的选择,但是没有自然线程可以限制此状态。
412
+
436
413
437
414
### Actors
438
415
439
- An [ 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,
440
- and a channel to communicate with other coroutines. A simple actor can be written as a function,
441
- but an actor with a complex state is better suited for a class.
416
+ 一个 [ actor] ( https://en.wikipedia.org/wiki/Actor_model ) 是由以下元素组成的一个实体:一个协程、它的状态受限封装在此协程中、以及一个与其他协程通信的 _ 通道_ 。一个简单的 actor 可以简单的写成一个函数,但是一个拥有复杂状态的 actor 更适合由类来表示。
442
417
443
- There is an [ actor] coroutine builder that conveniently combines actor's mailbox channel into its
444
- scope to receive messages from and combines the send channel into the resulting job object, so that a
445
- single reference to the actor can be carried around as its handle.
418
+ 有一个 [ actor] 协程构建器,它可以方便地将 actor 的 _ 邮箱通道_ 组合到其作用域中(用来接收消息)、组合发送 channel 与结果集对象,这样对 actor 的单个引用就可以作为其句柄持有。
446
419
447
- The first step of using an actor is to define a class of messages that an actor is going to process.
448
- Kotlin's [ sealed classes] ( https://kotlinlang.org/docs/reference/sealed-classes.html ) are well suited for that purpose.
449
- We define ` CounterMsg ` sealed class with ` IncCounter ` message to increment a counter and ` GetCounter ` message
450
- to get its value. The later needs to send a response. A [ CompletableDeferred] communication
451
- primitive, that represents a single value that will be known (communicated) in the future,
452
- is used here for that purpose.
420
+ 使用 actor 的第一步是定一个 actor 要处理的消息类。Kotlin 的 [ 密封类] ( https://kotlinlang.org/docs/reference/sealed-classes.html ) 很适合这种场景。我们使用 ` IncCounter ` 消息(用来递增计数器)和 ` GetCounter ` 消息(用来获取值)来定义 ` CounterMsg ` 密封类。后者需要发送回复。[ CompletableDeferred] 通信原语表示未来可知(可传达)的单个值,此处用于此目的。
453
421
454
422
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
455
423
@@ -462,7 +430,7 @@ class GetCounter(val response: CompletableDeferred<Int>) : CounterMsg() // a req
462
430
463
431
</div >
464
432
465
- Then we define a function that launches an actor using an [ actor] coroutine builder:
433
+ 接下来我们定义一个函数,使用 [ actor] 协程构建器来启动一个 actor:
466
434
467
435
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
468
436
@@ -481,7 +449,9 @@ fun CoroutineScope.counterActor() = actor<CounterMsg> {
481
449
482
450
</div >
483
451
484
- The main code is straightforward:
452
+
453
+ 主函数代码很简单:
454
+
485
455
486
456
<!-- - CLEAR -->
487
457
@@ -539,23 +509,19 @@ fun main() = runBlocking<Unit> {
539
509
540
510
</div >
541
511
542
- > You can get full code [ here ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-07.kt )
512
+ > 你可以点击 [ 这里 ] ( ../core/kotlinx-coroutines-core/test/guide/example-sync-07.kt ) 获得完整代码
543
513
544
514
<!-- - TEST ARBITRARY_TIME
545
515
Completed 100000 actions in xxx ms
546
516
Counter = 100000
547
517
-->
548
518
549
- It does not matter (for correctness) what context the actor itself is executed in. An actor is
550
- a coroutine and a coroutine is executed sequentially, so confinement of the state to the specific coroutine
551
- works 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).
519
+ actor 本身执行时所处上下文(就正确性而言)无关紧要。一个 actor 是一个协程,而一个协程是按顺序执行的,因此将状态限制到特定协程可以解决共享可变状态的问题。实际上, actor 可以修改自己的私有状态,但只能通过消息互相影响(避免任何锁定)。
520
+
521
+ actor 在高负载下比锁更有效,因为在这种情况下它总是有工作要做,而且根本不需要切换到不同的上下文。
552
522
553
- Actor is more efficient than locking under load, because in this case it always has work to do and it does not
554
- have to switch to a different context at all.
555
523
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.
524
+ > 注意, [ actor] 协程构建器是 [ produce] 协程构建器的双重构件。一个 actor 与它接收消息的通道相关联,而一个 producer 与它发送元素的通道相关联。
559
525
560
526
<!-- - MODULE kotlinx-coroutines-core -->
561
527
<!-- - INDEX kotlinx.coroutines -->
0 commit comments