[asynchronous] Kotlin 코 루틴에서 시작 / 가입과 async / await의 차이점은 무엇입니까?

에서 kotlinx.coroutines라이브러리 당신이 중 하나를 사용하여 새 코 루틴을 시작할 수 있습니다 launch(과 join) 또는 async(로를 await). 그들 사이의 차이점은 무엇입니까?



답변

  • launch코 루틴발사하고 잊는 데 사용됩니다 . 새 스레드를 시작하는 것과 같습니다. 내부 코드 launch가 예외로 종료 되면 일반적으로 백엔드 JVM 애플리케이션의 stderr에 인쇄되어 Android 애플리케이션과 충돌하는 스레드에서 포착되지 않은 예외로 처리 됩니다. join시작된 코 루틴의 완료를 기다리는 데 사용되며 예외를 전파하지 않습니다. 그러나 추락 된 자식 코 루틴은 해당 예외와 함께 부모를 취소합니다.

  • async일부 결과를 계산하는 코 루틴시작하는 데 사용됩니다 . 그 결과의 인스턴스로 표현된다 Deferred당신은 있어야 사용 await그것을. async코드 내부의 포착되지 않은 예외 는 결과 내부에 저장되며 Deferred다른 곳에서는 전달되지 않으며 처리되지 않으면 자동으로 삭제됩니다. async로 시작한 코 루틴을 잊지 마십시오 .


답변

이 가이드 https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md 가 유용하다는 것을 알았습니다 . 필수 부분을 인용하겠습니다

? 코 루틴

본질적으로 코 루틴은 가벼운 실입니다.

따라서 코 루틴을 스레드를 매우 효율적인 방식으로 관리하는 것으로 생각할 수 있습니다.

? 발사

fun main(args: Array<String>) {
    launch { // launch new coroutine in background and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello,") // main thread continues while coroutine is delayed
    Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}

따라서 launch백그라운드 스레드를 시작하고 무언가를 수행하고 즉시 토큰을로 반환합니다 Job. 이 스레드가 완료 될 때까지 차단 join하기 Job위해 이것을 호출 할 수 있습니다launch

fun main(args: Array<String>) = runBlocking<Unit> {
    val job = launch { // launch new coroutine and keep a reference to its Job
        delay(1000L)
        println("World!")
    }
    println("Hello,")
    job.join() // wait until child coroutine completes
}

? 비동기

개념적으로 비동기는 시작과 같습니다. 그것은 다른 모든 코 루틴과 동시에 작동하는 가벼운 실인 별도의 코 루틴을 시작합니다. 차이점은 launch가 Job을 반환하고 결과 값을 전달하지 않는 반면 async는 Deferred를 반환한다는 것입니다. 이는 나중에 결과를 제공하겠다는 약속을 나타내는 경량 비 차단 미래입니다.

따라서 async백그라운드 스레드를 시작하고 무언가를 수행하고 즉시 토큰을로 반환합니다 Deferred.

fun main(args: Array<String>) = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = async { doSomethingUsefulOne() }
        val two = async { doSomethingUsefulTwo() }
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")
}

지연된 값에 .await ()을 사용하여 최종 결과를 얻을 수 있지만 지연은 작업이기도하므로 필요한 경우 취소 할 수 있습니다.

그래서 Deferred사실이다 Job. https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html을 참조 하십시오.

interface Deferred<out T> : Job (source)

? 비동기는 기본적으로 열망합니다.

CoroutineStart.LAZY 값을 가진 선택적 시작 매개 변수를 사용하여 비 동기화하는 게으름 옵션이 있습니다. 일부 대기자가 결과를 필요로하거나 시작 함수가 호출 된 경우에만 코 루틴을 시작합니다.


답변

launchasync새로운 코 루틴을 시작하는 데 사용됩니다. 그러나 그들은 다른 방식으로 실행합니다.

차이점을 매우 쉽게 이해하는 데 도움이되는 매우 기본적인 예를 보여 드리고자합니다

  1. 시작하다
    class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btnCount.setOnClickListener {
            pgBar.visibility = View.VISIBLE
            CoroutineScope(Dispatchers.Main).launch {
                val currentMillis = System.currentTimeMillis()
                val retVal1 = downloadTask1()
                val retVal2 = downloadTask2()
                val retVal3 = downloadTask3()
                Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1}, ${retVal2}, ${retVal3} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
                pgBar.visibility = View.GONE
            }
        }

    // Task 1 will take 5 seconds to complete download
    private suspend fun downloadTask1() : String {
        kotlinx.coroutines.delay(5000);
        return "Complete";
    }

    // Task 1 will take 8 seconds to complete download    
    private suspend fun downloadTask2() : Int {
        kotlinx.coroutines.delay(8000);
        return 100;
    }

    // Task 1 will take 5 seconds to complete download
    private suspend fun downloadTask3() : Float {
        kotlinx.coroutines.delay(5000);
        return 4.0f;
    }
}

이 예제에서 내 코드는 btnCount버튼 을 클릭하면 3 개의 데이터를 다운로드 하고 pgBar모든 다운로드가 완료 될 때까지 진행률 표시 줄을 표시합니다. 3 개 있습니다 suspend기능 downloadTask1(), downloadTask2()downloadTask3()다운로드 데이터. 시뮬레이션하기 위해 delay()이러한 기능을 사용 했습니다. 이 기능을 기다립니다 5 seconds, 8 seconds그리고 5 seconds각각.

launch이러한 일시 중단 기능을 시작하는 데 사용한 것처럼 순차적으로 하나씩launch 실행됩니다 . 이는 완료 후 시작되고 완료된 후에 만 시작됨을 의미합니다 .downloadTask2()downloadTask1()downloadTask3()downloadTask2()

출력 스크린 샷에서와 같이 Toast3 회 다운로드를 모두 완료하는 데 걸리는 총 실행 시간은 5 초 + 8 초 + 5 초 = 18 초 입니다.launch

발사 예

  1. 비동기

우리가 보았 듯이 3 가지 작업 모두에서 launch실행 sequentially됩니다. 모든 작업을 완료하는 데 시간이 걸렸습니다 18 seconds.

이러한 작업이 독립적이고 다른 작업의 계산 결과가 필요하지 않은 경우 실행할 수 있습니다 concurrently. 동시에 시작하여 백그라운드에서 동시에 실행됩니다. 이 작업을 수행 할 수 있습니다 async.

asyncsuspend 함수가 반환하는 데이터 유형 인 Deffered<T>유형 의 인스턴스를 T반환합니다. 예를 들어

  • downloadTask1()반환 Deferred<String>문자열로하는 함수의 반환 형식입니다
  • downloadTask2()반환 Deferred<Int>(int)로하는 기능의 반환 형식입니다
  • downloadTask3()반환 Deferred<Float>플로트로하는 함수의 반환 형식입니다

async유형 의 return 객체를 사용하여 유형 Deferred<T>의 반환 값을 얻을 수 T있습니다. 그것은 await()전화로 할 수 있습니다 . 예를 들어 아래 코드를 확인하십시오.

        btnCount.setOnClickListener {
        pgBar.visibility = View.VISIBLE

        CoroutineScope(Dispatchers.Main).launch {
            val currentMillis = System.currentTimeMillis()
            val retVal1 = async(Dispatchers.IO) { downloadTask1() }
            val retVal2 = async(Dispatchers.IO) { downloadTask2() }
            val retVal3 = async(Dispatchers.IO) { downloadTask3() }

            Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1.await()}, ${retVal2.await()}, ${retVal3.await()} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
            pgBar.visibility = View.GONE
        }

이런 식으로, 우리는 3 가지 작업을 동시에 시작했습니다. 따라서 완료하는 데 걸리는 총 실행 시간 은 3 개의 작업 중 가장 큰 8 seconds시간에 불과 downloadTask2()합니다. 당신은 이것을 다음 스크린 샷에서 볼 수 있습니다Toast message

예를 기다리다


답변

  1. 코 루틴 빌더, 즉 launch 및 async는 기본적으로 CoroutineScope 유형의 리시버를 가진 람다입니다. 즉, 내부 블록이 일시 중단 기능으로 컴파일되므로 둘 다 비동기 모드로 실행되며 둘 다 블록을 순차적으로 실행합니다.

  2. 실행과 비동기의 차이점은 두 가지 가능성을 가능하게한다는 것입니다. 실행 빌더는 작업을 리턴하지만 비동기 함수는 지연된 오브젝트를 리턴합니다. launch를 사용하면 데이터베이스에 쓰거나 파일을 저장하거나 기본적으로 부작용으로 호출 된 것을 처리하는 등 반환 된 값을 기대하지 않는 블록을 실행할 수 있습니다. 반면에 앞서 언급했듯이 Deferred를 반환하는 async는 데이터를 래핑하는 객체 인 블록의 실행에서 유용한 값을 반환하므로 주로 결과뿐만 아니라 부작용에도 사용할 수 있습니다. NB : await 함수를 사용하여 지연된 값을 제거하고 값을 얻을 수 있습니다. 그러면 값이 반환되거나 예외가 발생할 때까지 명령문의 실행이 차단됩니다!

  3. 코 루틴 빌더 (시작 및 비동기)는 모두 취소 가능합니다.

  4. 아무것도 더? : 블록 내에서 예외가 발생하면 실행으로 코 루틴이 자동으로 취소되고 예외가 전달됩니다. 반면에 비동기로 발생하면 예외가 더 이상 전파되지 않으며 반환 된 Deferred 객체 내에서 포착 / 처리되어야합니다.

  5. 코 루틴에 대한 자세한 내용 https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html
    https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1


답변

발사 는 직업을 반환

async 는 결과를 반환합니다 (지연된 작업).

join with launch는 작업이 완료 될 때까지 대기하는 데 사용됩니다. join ()을 호출하는 코 루틴을 일시 중단하여 현재 스레드가 다른 작업 (예 : 다른 코 루틴 실행)을 자유롭게 수행 할 수 있습니다.

async 는 일부 결과를 계산하는 데 사용됩니다. 코 루틴을 작성하고 향후 결과를 지연 구현으로 리턴합니다. 결과 지연이 취소되면 실행중인 코 루틴이 취소됩니다.

문자열 값을 반환하는 비동기 메서드를 고려하십시오. async 메소드를 await없이 사용하면 Deferred 문자열을 반환하지만 await를 사용하면 결과로 문자열을 얻습니다.

비동기와 실행의 주요 차이점 지연은 코 루틴 실행이 완료된 후 T 유형의 특정 값을 반환하지만 작업은 그렇지 않습니다.


답변

비동기 대 발사
Diff 이미지 대 비동기

발사 / 비동기 결과 없음

  • 결과가 필요 없을 때 사용
  • 호출되는 코드를 차단하지 마십시오.
  • 병렬로 실행

결과에 대한 비동기

  • 결과를 기다려야하고 효율성을 위해 병렬로 실행할 수있는 경우
  • 호출되는 코드를 차단
  • 병렬로 실행

답변