Skip to content
GitLab
Menu
Why GitLab
Pricing
Contact Sales
Explore
Why GitLab
Pricing
Contact Sales
Explore
Sign in
Get free trial
Changes
Page history
Update Kotlin coroutines
authored
Jul 30, 2023
by
umaumax
Hide whitespace changes
Inline
Side-by-side
Kotlin-coroutines.md
View page @
8b77dd88
...
@@ -5,6 +5,205 @@
...
@@ -5,6 +5,205 @@
*
新しいコルーチンを作成して、即座に実行を開始する
*
新しいコルーチンを作成して、即座に実行を開始する
*
返り値の返却はできない
*
返り値の返却はできない
## async
*
`async`
ブロックは非同期タスク(Deferred オブジェクト)を生成し、
`await`
で返り値を受け取る
### 複数のasyncブロックの非同期タスクを生成して実行するコード例
下記のコードでは、asyncブロックの非同期タスクの結果はforループの単位(100単位)で表示される
```
kotlin
import
kotlinx.coroutines.*
// import kotlin.system.exitProcess
fun
main
(
args
:
Array
<
String
>)
=
runBlocking
{
val
numberOfBlocks
=
100
var
x
=
0
val
jobs
=
List
(
numberOfBlocks
)
{
async
{
println
(
"start"
)
// yield() // ここでコルーチンが切り替わるとstartが連続して表示される
for
(
i
in
1
..
100
)
{
// val pre_x = x
// yield() // ここでコルーチンが切り替わるとresultの値が1単位で変化する可能性が非常に高い かつ この行の前後の`x`が代わり得る
x
+=
1
// if (pre_x + 1 != x) {
// exitProcess(0)
// }
}
println
(
"end"
)
x
}
}
val
results
=
jobs
.
awaitAll
()
for
(
i
in
0
until
numberOfBlocks
)
{
println
(
"async block[$i]'s result: ${results[i]}"
)
}
}
```
上記の
`x`
は1スレッドに限定されたコルーチン内で動作する限りは正しい結果を返すが、複数スレッドで同時実行されるコルーチンとなると、
`yield()`
の有無に関係なく未定義な動作となる
`val dispatcher = newFixedThreadPoolContext(10, "HogeThread")`
を宣言し、
`async()`
を
`async(dispatcher)`
へ変更して、
`dispatcher.close()`
をすることで、スレッドプールでコルーチンを実行して確かめることができる
`import java.util.concurrent.atomic.*`
を宣言し、
`var x = 0`
を
`var x = AtomicInteger()`
、
`x += 1`
を
`x.incrementAndGet()`
とすると、期待した結果となる(ただし、awaitの返り値はすべて10000である可能性が高い)
## Dispatchers
[
Dispatchers.Default
](
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
)
スレッドの最大数はCPUコアの数と同じ(ただし、2並列以上)
デフォルトで提供されるディスパッチャーでコルーチンを実行させたい場合は
`async(Dispatchers.Default)`
、
`withContext(Dispatchers.Default)`
、
`launch(Dispatchers.Default)`
などとする
[
Dispatchers.Main
](
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
)
これはmain関数を実行しているスレッドではなく、UIを制御しているスレッドを指す
```
kotlin
launch
()
{
// context of the parent
println
(
"launch(): ${Thread.currentThread().name}"
)
}
launch
(
Dispatchers
.
Unconfined
)
{
// not confined -- context of the parent(ただし、最初のsuspend地点まで現在のコンテキストで、それ以降のコンテキストは保証されない)
println
(
"launch(Dispatchers.Unconfined): ${Thread.currentThread().name}"
)
}
launch
(
Dispatchers
.
Default
)
{
// will get dispatched to DefaultDispatcher
println
(
"launch(Dispatchers.Default): ${Thread.currentThread().name}"
)
}
```
出力(異なるスレッドで実行されるので、ランダムな出力順番となる)
```
bash
launch
(
Dispatchers.Unconfined
)
: main
launch
(
Dispatchers.Default
)
: DefaultDispatcher-worker-1
launch
()
: main
```
[
Coroutine context and dispatchers | Kotlin Documentation
](
https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html#unconfined-vs-confined-dispatcher
)
`Dispatchers.Unconfined`
は最初のsuspend地点まで現在のコンテキストで、それ以降のコンテキストは保証されない
```
kotlin
launch
(
Dispatchers
.
Unconfined
)
{
// not confined -- will work with main thread
println
(
"Unconfined : I'm working in thread ${Thread.currentThread().name}"
)
delay
(
500
)
println
(
"Unconfined : After delay in thread ${Thread.currentThread().name}"
)
}
launch
{
// context of the parent, main runBlocking coroutine
println
(
"main runBlocking: I'm working in thread ${Thread.currentThread().name}"
)
delay
(
1000
)
println
(
"main runBlocking: After delay in thread ${Thread.currentThread().name}"
)
}
```
```
bash
Unconfined : I
'm working in thread main
main runBlocking: I'
m working
in
thread main
Unconfined : After delay
in
thread kotlinx.coroutines.DefaultExecutor
main runBlocking: After delay
in
thread main
```
## コルーチンの並列度
rustのtokioと同じように明示的に別のスレッドを利用しない限り、コルーチンの同時実行数は1である
## スレッドプールを作成してコルーチンを実行する例
```
kotlin
import
kotlinx.coroutines.*
fun
main
()
=
runBlocking
{
// このスレッドプール数を1にしても、mainスレッドとは別のスレッドで実行されることとなる
val
dispatcher1
=
newFixedThreadPoolContext
(
3
,
"HogeThread"
)
val
dispatcher2
=
newFixedThreadPoolContext
(
2
,
"FugaThread"
)
println
(
"main thread: ${Thread.currentThread().name}(id:${Thread.currentThread().getId()})"
)
val
job1
=
async
(
dispatcher1
)
{
for
(
i
in
1
..
9
)
{
println
(
"Job Hoge: $i, thread: ${Thread.currentThread().name}(id:${Thread.currentThread().getId()})"
)
}
}
val
job2
=
async
(
dispatcher2
)
{
for
(
i
in
1
..
9
)
{
println
(
"Job Fuga: $i, thread: ${Thread.currentThread().name}(id:${Thread.currentThread().getId()})"
)
}
}
job1
.
await
()
job2
.
await
()
dispatcher1
.
close
()
dispatcher2
.
close
()
}
```
```
bash
main thread: main
(
id
:1
)
Job Hoge: 1, thread: HogeThread-1
(
id
:14
)
Job Hoge: 2, thread: HogeThread-1
(
id
:14
)
Job Hoge: 3, thread: HogeThread-1
(
id
:14
)
Job Hoge: 4, thread: HogeThread-1
(
id
:14
)
Job Hoge: 5, thread: HogeThread-1
(
id
:14
)
Job Fuga: 1, thread: FugaThread-1
(
id
:15
)
Job Fuga: 2, thread: FugaThread-1
(
id
:15
)
Job Hoge: 6, thread: HogeThread-1
(
id
:14
)
Job Hoge: 7, thread: HogeThread-1
(
id
:14
)
Job Fuga: 3, thread: FugaThread-1
(
id
:15
)
Job Fuga: 4, thread: FugaThread-1
(
id
:15
)
Job Fuga: 5, thread: FugaThread-1
(
id
:15
)
Job Fuga: 6, thread: FugaThread-1
(
id
:15
)
Job Fuga: 7, thread: FugaThread-1
(
id
:15
)
Job Hoge: 8, thread: HogeThread-1
(
id
:14
)
Job Hoge: 9, thread: HogeThread-1
(
id
:14
)
Job Fuga: 8, thread: FugaThread-1
(
id
:15
)
Job Fuga: 9, thread: FugaThread-1
(
id
:15
)
```
## 単一のスレッドでコルーチンを実行する
`@OptIn`
が必要となるので、
`val dispatcher = newFixedThreadPoolContext(1, "HogeThread")`
としたほうがよいかもしれない
これは単一のスレッドで実行することで、コルーチンで安全に変数を操作できる(主にUIアプリケーションで使わている)
```
kotlin
import
kotlinx.coroutines.*
@OptIn
(
kotlinx
.
coroutines
.
ExperimentalCoroutinesApi
::
class
)
fun
main
(
args
:
Array
<
String
>)
=
runBlocking
{
val
singleContext
=
newSingleThreadContext
(
"singleContext"
)
withContext
(
singleContext
)
{
println
(
"hoge"
)
}
}
```
## コンテキストの切り替えタイミング
連続して複数のasyncタスクをawaitで待ち受けるときにasyncブロックの非同期タスクの冒頭で、
`yield()`
や
`delay()`
を利用して切り替えるタイミングの有無で挙動が変わる
例えば、切り替えタイミングがない場合は
```
start 1
end 1
start 2
end 2
start 3
end 3
```
となるが、切り替えタイミングがある場合は
```
start 1
start 2
start 3
end 1
end 2
end 3
```
となる
## トラブルシューティング
## トラブルシューティング
### Unresolved reference: launch
### Unresolved reference: launch
*
`launch`
は
`runBlocking`
や
`coroutineScope`
などのスコープ内でしか利用できない
*
`launch`
は
`runBlocking`
や
`coroutineScope`
などのスコープ内でしか利用できない
...
...
...
...