CoroutineContextはコルーチンで起動されるスレッドの属性を格納しています。
その中にコルーチンの名前を表現するName属性があります。
Name属性を出力する方法を紹介します。
目次 [非表示]
CoroutineContextのName属性
CoroutineContextは次のような属性を格納しています。
属性名 | Key | 概要 |
---|---|---|
Job | Job | 自身が管理しているスレッドのハンドラー |
Dispatchers | ContinuationInterceptor | スレッドの取得先(スレッドプール名) |
Name | CoroutineName | コルーチンの名前(デバッグ用) |
ExceptionHandler | CoroutineExceptionHandler | 例外通知のハンドラー |
※スレッドのハンドラー:スレッドに対して指示を出す場合の窓口 |
この中のName属性はコルーチンへ付けられた名前を表現します。
用途はデバッグでコルーチンを識別するためです。ただし、デフォルトは出力されません。
Name属性を設定
Name属性を設定する方法は幾つかあります。
CoroutineScopeの実装時
CoroutineScopeを実装する時に設定します。
1 2 3 | public interface CoroutineScope { public val coroutineContext: CoroutineContext } |
1 2 3 4 | class SampleScope : CoroutineScope { override val coroutineContext: CoroutineContext = Job() + Dispatchers.Default + CoroutineName( "Hoge" ) // Contextを定義 } |
ビルダーまたはwithContextの引数
ビルダーまたはwithContextの引数で設定します。
1 2 3 4 5 6 | scope = SampleScope() scope?.launch { launch(CoroutineName( "Fuga" )) { ... } async(CoroutineName( "Pico" )) { ... }.await() withContext(CoroutineName( "KoKo" )) { ... } } |
Name属性の出力
currentThread( )関数を使ってスレッド名を取得しても、デフォルトはコルーチン名を出力しません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // System.setProperty("kotlinx.coroutines.debug", "on" ) findViewById<Button>(R.id.btnStart).setOnClickListener { scope = SampleScope() scope?.launch(Dispatchers.Default) { println( "Start[DEFAULT] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } scope?.async(Dispatchers.Main) { println( "Start[NAIN] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } scope?.launch { withContext(Dispatchers.IO) { println( "Start[IO] Thread = ${getThread()}" ) } Thread.sleep( 1000 ) // 重い処理の代わり } } |
1 2 3 | inline private fun getThread(): String { return Thread.currentThread().name // カレントのスレッド名 } |
Start[NAIN] Thread = main Start[IO] Thread = DefaultDispatcher-worker-1 Start[DEFAULT] Thread = DefaultDispatcher-worker-2
コルーチン名はdebugスイッチをonに切り替えることにより出力されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | System.setProperty( "kotlinx.coroutines.debug" , "on" ) findViewById<Button>(R.id.btnStart).setOnClickListener { scope = SampleScope() scope?.launch(Dispatchers.Default) { println( "Start[DEFAULT] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } scope?.async(Dispatchers.Main) { println( "Start[NAIN] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } scope?.launch { withContext(Dispatchers.IO) { println( "Start[IO] Thread = ${getThread()}" ) } Thread.sleep( 1000 ) // 重い処理の代わり } } |
Start[DEFAULT] Thread = DefaultDispatcher-worker-1 @Hoge#1 Start[NAIN] Thread = main @Hoge#2 Start[IO] Thread = DefaultDispatcher-worker-2 @Hoge#3
サンプルは全て同じCoroutineScope(例はSampleScope)を起点にコルーチンを開始しているので、同じコルーチン名(例は”Hoge”)になっています。
コルーチンの番号
コルーチンの番号は、コルーチンが開始される毎に付けられる通し番号です。
Dispatchersと無関係
コルーチンの番号はDispatchers(スレッドの取得先)と無関係です。通し番号になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | System.setProperty( "kotlinx.coroutines.debug" , "on" ) findViewById<Button>(R.id.btnStart).setOnClickListener { scope = SampleScope() scope?.launch(Dispatchers.Default) { println( "Start[DEFAULT] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } scope?.async(Dispatchers.Main) { println( "Start[NAIN] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } scope?.launch(Dispatchers.Default) { println( "Start[DEFAULT] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } scope?.async(Dispatchers.Main) { println( "Start[NAIN] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } } |
Start[DEFAULT] Thread = DefaultDispatcher-worker-1 @Hoge#1 Start[DEFAULT] Thread = DefaultDispatcher-worker-2 @Hoge#3 Start[NAIN] Thread = main @Hoge#2 Start[NAIN] Thread = main @Hoge#4
コルーチンの親子関係と無関係
コルーチンの番号は入れ子になった場合の親子関係に無関係です。通し番号になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | System.setProperty( "kotlinx.coroutines.debug" , "on" ) findViewById<Button>(R.id.btnStart).setOnClickListener { scope = SampleScope() scope?.launch { println( "Star[parent] Thread = ${getThread()}" ) launch { println( " Star[child:0] Thread = ${getThread()}" ) launch { println( " Star[child:1] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } Thread.sleep( 1000 ) // 重い処理の代わり } Thread.sleep( 1000 ) // 重い処理の代わり } } |
Star[parent] Thread = DefaultDispatcher-worker-1 @Hoge#1 Star[child:0] Thread = DefaultDispatcher-worker-2 @Hoge#2 Star[child:1] Thread = DefaultDispatcher-worker-3 @Hoge#3
non-blocking動作時は同じ番号
Suspend関数でスレッドが一時停止したあと再開する場合、スレッドは再取得されます。しかし、新たなコルーチンが開始されるわけではありません。コルーチンの番号は同じ番号になります。
1 2 3 4 5 6 7 8 9 10 11 12 | System.setProperty( "kotlinx.coroutines.debug" , "on" ) findViewById<Button>(R.id.btnStart).setOnClickListener { scope = SampleScope() scope?.launch { println( "Star[State:0] Thread = ${getThread()}" ) delay( 1000 ) // スレッドの一時停止 println( "Star[State:1] Thread = ${getThread()}" ) delay( 1000 ) // スレッドの一時停止 println( "Star[State:2] Thread = ${getThread()}" ) } } |
Star[State:0] Thread = DefaultDispatcher-worker-2 @Hoge#1 Star[State:1] Thread = DefaultDispatcher-worker-2 @Hoge#1 Star[State:2] Thread = DefaultDispatcher-worker-2 @Hoge#1
withContextのケースは同じ番号
withContextはasync(+await)と同様な動作です。しかし、コルーチンが開始されるわけではありません。コルーチンの番号は同じ番号になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | System.setProperty( "kotlinx.coroutines.debug" , "on" ) findViewById<Button>(R.id.btnStart).setOnClickListener { scope = SampleScope() scope?.launch { withContext(Dispatchers.Default) { println( "Star[withContext:0] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } withContext(Dispatchers.Main) { println( "Star[withContext:1] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } withContext(Dispatchers.IO) { println( "Star[withContext:2] Thread = ${getThread()}" ) Thread.sleep( 1000 ) // 重い処理の代わり } } } |
Star[withContext:0] Thread = DefaultDispatcher-worker-2 @Hoge#1 Star[withContext:1] Thread = main @Hoge#1 Star[withContext:2] Thread = DefaultDispatcher-worker-2 @Hoge#1
関連記事: