経緯
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を使用するのが良さそうです。
少しスッキリしました。