2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Kotlin coroutineのasync/awaitとwithContextについて

Posted at

経緯

coroutineを使用していてなんとなく疑問に思っていたのが、
async/awaitとwithContextの違いについてでした。
少し調べてみましたが、かなり不足のある記事になると思うので、加筆前提で投稿しようと思います。

coroutineとは

非同期処理のできるスレッドのようなもので、中断・再開などができます。
軽量な点がかなり嬉しいですね。
余談ですが昔Unity C#で開発していた時にもcoroutineを頻繁に使用していました。
あちらは次フレームまでの待機などでしたが。

サンプル

fun sample(){
    GlobalScope.launch {
        asyncTest()
        withContextTest()
    }
}

// 1秒待ってから1 <= n <= 99 のn:Intを返却する
private fun getRandomNum(): Int {
    Thread.sleep(1000)
    return (1..99).random()
}

private suspend fun asyncTest(){
    Log.d("test", "asyncTest start.")

    val hoge = GlobalScope.async(Dispatchers.Default) {
        getRandomNum()
    }.await()

    Log.d("test", "asyncTest finish. num is $hoge.")
}

private suspend fun withContextTest() {
    Log.d("test", "withContextTest start.")

    val hoge = withContext(Dispatchers.Default) {
        getRandomNum()
    }

    Log.d("test", "withContextTest finish. num is $hoge.")
}

上記のサンプルを実行した場合、
asyncTest()、withContextTest()の出力結果(順)に違いはほとんど見られません。
withContextの方が少しパフォーマンスが良い感じでしょうか。

2021-11-14 10:46:29.211 9618-9645/com.example.testapplication D/test: asyncTest start.
2021-11-14 10:46:30.217 9618-9646/com.example.testapplication D/test: asyncTest finish. num is 53.
2021-11-14 10:46:30.217 9618-9646/com.example.testapplication D/test: withContextTest start.
2021-11-14 10:46:31.222 9618-9646/com.example.testapplication D/test: withContextTest finish. num is 76.

違いについて

ここで思い出したのが、以下の違い。

async
・新規コルーチンを開始する

withContext
・スレッドを切り替えるよう指示ができる

参考:コルーチンを開始するwithContext() のパフォーマンス

asyncは新たにコルーチンを開始し別のスレッドで処理を行い
withContextはコルーチンを作成せずスレッドを切り替えて処理を行う
ということですね。

asyncはawait()を呼ぶことで処理を実行し結果を得ることができます。
withContextの場合はその場で処理を待つイメージでしょうか。

サンプルを少しいじるとわかりやすいかも。

private suspend fun asyncTest() {
    Log.d("test", "asyncTest start.")

    val hoge = GlobalScope.async(Dispatchers.Default) {
        getRandomNum()
    }

    val fuga = GlobalScope.async(Dispatchers.Default) {
        getRandomNum()
    }

    val totalNum = hoge.await() + fuga.await()
    Log.d("test", "asyncTest finish. totalNum is $totalNum.")
}

private suspend fun withContextTest() {
    Log.d("test", "withContextTest start.")

    val hoge = withContext(Dispatchers.Default) {
        getRandomNum()
    }

    val fuga = withContext(Dispatchers.Default) {
        getRandomNum()
    }

    val totalNum = hoge + fuga
    Log.d("test", "withContextTest finish. totalNum is $totalNum.")
}

2021-11-14 11:44:08.527 13859-13884/com.example.testapplication D/test: asyncTest start.
2021-11-14 11:44:09.531 13859-13885/com.example.testapplication D/test: asyncTest finish. totalNum is 71.
2021-11-14 11:44:09.531 13859-13885/com.example.testapplication D/test: withContextTest start.
2021-11-14 11:44:11.537 13859-13885/com.example.testapplication D/test: withContextTest finish. totalNum is 117.

asyncはおよそ1秒で実行されていますね。並列。withContextはおよそ2秒なので直列ですね。

結論

並列処理を必要とする場合はasync/await、そうでない場合はwithContextを使用するのが良さそうです。
少しスッキリしました。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?