Help us understand the problem. What is going on with this article?

【Kotlin】Coroutineを理解する

More than 1 year has passed since last update.

はじめに

Kotlin 1.1からCoroutineが実験的に組み込まれました。
Coroutineとは「特定のスレッドに束縛されない、中断可能な計算インスタンス」です。
非同期処理で用いられますが、Threadよりも軽量で、実行途中で処理を中断・再開することができます。

準備

Coroutineを利用するために、build.gradleに下記の情報を追記します。
今回は2018/09/30時点での最新版(0.30.0)を使用しています。
更新状況はここから確認できます。

build.gradle
dependencies {
    ...
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.30.0"
}
build.gradle
kotlin {
    experimental {
        coroutines 'enable'
    }
}
build.gradle
repositories {
    jcenter()
}

使い方

Coroutineの作成にはCoroutineビルダーを使用します。

runBlocking

現在のスレッドをブロックするビルダーです。任意の型を返します。

runBlocking {
    // 何かしらの処理
}

Sample

fun main(args:Array<String>) {
    println(1)
    runBlocking {
        println(2)
    }
    println(3)
}

/**
 * 実行結果
 * 1
 * 2
 * 3
 */

launch

戻り値としてJobを返すビルダーです。

0.26.1からlaunchGlobalScope.launchになりました。

GlobalScope.launch {
    // 何かしらの処理 
}

Sample

現在のスレッドをブロックしないため、launch内の処理が実行される前にプログラムが終了します。

fun main(args:Array<String>) {
    println(1)
    launch {
        println(2)
    }
    println(3)
}

/**
 * 実行結果
 * 1
 * 3
 */

引数で実行するスレッドを指定できます。
引数なしの場合はDispatchers.Defaultが設定されます。

0.26.1からCommonPoolDispatchers.Defaultになりました。
0.26.1からUIDispatchers.Unconfinedになりました。

/**
 * Dispatchers.Default -> バックグラウンドスレッド
 * Dispatchers.Unconfined -> メインスレッド
 */
GlobalScope.launch(Dispatchers.Unconfined) {
    // 何かしらの処理 
}

Job#join()を利用すれば、呼び出し先のcoroutineの終了まで呼び出し元のcoroutineを中断できます。

runBlocking {
    GlobalScope.launch() {
        // 何かしらの処理 
    }.join()
}

Sample

fun main(args:Array<String>) = runBlocking {
    println(1)
    GlobalScope.launch {
        println(2)
    }.join()
    println(3)
}

/**
 * 実行結果
 * 1
 * 2
 * 3
 */

async

戻り値としてDeferred<T>を返すビルダーです。
Coroutineの処理が終わったタイミングで戻り値を取得できます。

0.26.1からasyncGlobalScope.asyncになりました。

GlobalScope.async {
    // 何かしらの処理 
}

Sample

launch同様、現在のスレッドをブロックしないため、async内の処理が実行されません。

fun main(args:Array<String>) {
    println(1)
    GlobalScope.async {
        println(2)
    }
    println(3)
}

/**
 * 実行結果
 * 1
 * 3
 */

Job#join()同様、Deferred<T>#await()を利用すれば、呼び出し先のCoroutineの終了まで呼び出し元のCoroutineを中断できます。

runBlocking {
    GlobalScope.async {
        // 何かしらの処理 
    }.await()
}

Sample

fun main(args:Array<String>) = runBlocking {
    println(1)
    GlobalScope.async {
        println(2)
    }.await()
    println(3)
}

/**
 * 実行結果
 * 1
 * 2
 * 3
 */

Suspend関数

Coroutineを中断できる関数です。suspendをつけることで宣言できます。
CoroutineかSuspend関数内からのみ実行できます。

suspend fun sampleSuspending(){
     // 何かしらの処理       
}

Sample

delayはSuspend関数なので実行できます。

fun main(args:Array<String>) = runBlocking {
    println(1)
    sampleSuspending()
    println(3)
}

suspend fun sampleSuspending() {
    delay(1000)
    println(2) 
}

/**
 * 実行結果
 * 1
 * (1秒中断)
 * 2
 * 3
 */ 

まとめ

非同期処理を直感的に理解しやすく、とても簡潔に記述できるようになりました。

参考

Coroutines - Kotlin Programming Language
AndroidでKotlin Coroutinesを使ってみる
eaglesakuraの技術ブログ
Kotlin の Coroutine を概観する
入門Kotlin coroutines

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした