runBlocking は、通常の同期コードからコルーチンを開始するための関数です。
そして、開始したコルーチンの処理が完了するまで 呼び出し元のスレッドをブロック(停止)して待機します。
つまり runBlocking は、次のような役割を持つ関数です。
- 同期コード → コルーチン世界へ入る
- コルーチンの完了を待つ
- 完了するまでスレッドをブロックする
このため runBlocking は 同期処理とコルーチンの橋渡し(ブリッジ)として利用されます。
基本構文
import kotlinx.coroutines.*
fun main() = runBlocking {
println("Start")
launch {
delay(1000)
println("Coroutine finished")
}
println("End")
}
このコードの実行順序は次の通りです。
runBlockingが開始されるlaunchによって子コルーチンが起動するdelayにより1秒待機- 子コルーチンが終了する
runBlockingが終了する
重要なのは、子コルーチンがすべて終了するまで runBlocking は戻らないという点です。
runBlockingの特徴
呼び出し元スレッドをブロックする
通常のコルーチンは、スレッドをブロックしません。
例えば launch や async は非同期で実行されます。
しかし runBlocking は例外で、呼び出し元スレッドを停止させて処理完了を待ちます。
比較すると次のようになります。
| 関数 | スレッドの扱い |
|---|---|
| launch | ブロックしない |
| async | ブロックしない |
| runBlocking | ブロックする |
suspend関数を呼び出せる
suspend 関数は、通常の関数から直接呼び出すことはできません。
例えば次のコードはコンパイルエラーになります。
suspend fun fetchData(): String {
delay(1000)
return "data"
}
fun main() {
fetchData() // エラー
}
しかし runBlocking を使うと呼び出せます。
fun main() = runBlocking {
val result = fetchData()
println(result)
}
これは runBlocking が CoroutineScope を作り、その中でコルーチンを実行するためです。
runBlockingの動作イメージ
runBlocking は内部的に次のような処理を行います。
- 新しいコルーチンスコープを作る
- コルーチンを起動する
- 呼び出し元スレッドをブロックする
- コルーチンが終了するまで待つ
- 完了後にスレッドを再開する
つまり構造としては次のようになります。
main thread
│
└ runBlocking
│
├ launch
├ async
└ suspend functions
runBlocking は、このスコープ内のすべての子コルーチンの完了を待ちます。
スレッドの挙動
runBlocking は 呼び出し元スレッドをブロックします。
例えば次のコードを見てみます。
fun main() = runBlocking {
println(Thread.currentThread().name)
launch {
println(Thread.currentThread().name)
}
}
多くの場合、出力は次のようになります。
main
main
これは、子コルーチンが 明示的なDispatcherを指定していない場合、同じスレッド上で実行されることが多いためです。
ただし、別のDispatcherを指定すれば別スレッドで実行されます。
fun main() = runBlocking {
launch(Dispatchers.Default) {
println(Thread.currentThread().name)
}
}
この場合は、DefaultDispatcher のスレッドで実行されます。
Dispatcherを使った処理
重い処理やI/O処理を別スレッドで実行したい場合は、withContext を使うことが一般的です。
fun main() = runBlocking {
withContext(Dispatchers.IO) {
println(Thread.currentThread().name)
}
}
このコードでは
runBlockingは呼び出し元スレッドをブロックするwithContext(Dispatchers.IO)はIOスレッドで処理を行う
という動作になります。
runBlockingの主な用途
runBlocking は、主に次のような場面で使用されます。
main関数
コマンドラインアプリケーションでは、main からコルーチンを開始するために使われることがあります。
fun main() = runBlocking {
val data = fetchData()
println(data)
}
テストコード
コルーチンを使ったコードをテストする際にも利用できます。
@Test
fun testApi() = runBlocking {
val result = fetchData()
assertEquals("ok", result)
}
ただし現在のコルーチンテストでは、kotlinx-coroutines-test の runTest が使われることも多くなっています。
同期コードとの橋渡し
Javaの同期APIや既存コードからコルーチンを呼び出す場合にも使われます。
同期コード
↓
runBlocking
↓
コルーチン処理
Androidでの使用
Androidアプリでは、runBlocking を通常のアプリ処理で使うべきではありません。
理由は、スレッドをブロックしてしまうためです。
例えばUIスレッドで次のコードを実行すると、
runBlocking {
delay(5000)
}
画面が5秒間停止してしまいます。
これは ANR(Application Not Responding) の原因になります。
Androidでは代わりに次の仕組みを使います。
lifecycleScope.launchviewModelScope.launch
これらは UIスレッドをブロックせずにコルーチンを実行できます。
注意点
suspend関数の中でrunBlockingを使わない
suspend 関数の中で runBlocking を使うのは通常避けるべきです。
suspend fun example() {
runBlocking {
delay(1000)
}
}
これはスレッドをブロックしてしまうため、
- コルーチンの利点を失う
- スレッド詰まり
- デッドロックの可能性
などの問題を引き起こす可能性があります。
まとめ
runBlocking は、コルーチンを同期コードから実行するためのブリッジ関数です。
主な特徴は次の通りです。
- コルーチンを開始する
- 完了するまで呼び出し元スレッドをブロックする
suspend関数を呼び出せる- コルーチンスコープを作る
- 子コルーチンの終了を待つ
主な用途は次の通りです。
main関数- コルーチンのテスト
- 同期コードとコルーチンの橋渡し
ただし、アプリケーションの通常処理やAndroidのUIスレッドでは 使用を避けるべき関数でもあります。
以上、KotlinのrunBlockingについてでした。
最後までお読みいただき、ありがとうございました。










