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


답변:


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

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


1
Async는 Android의 네트워크 통화에 적합한 코 루틴 빌더입니까?
Faraaz

올바른 코 루틴 제작자는 수행하려는 작업에 따라 다릅니다.
Roman Elizarov

9
"비동기 화로 시작한 코 루틴을 잊어서는 안된다"고 상세하게 설명 할 수 있습니까? 예를 들어 예상치 못한 문제가 있습니까?
Luis

2
"비동기 코드 내부의 포착되지 않은 예외는 결과 지연 내에 저장되며 다른 곳에서는 전달되지 않으며 처리되지 않으면 자동으로 삭제됩니다."
로마 엘리자 로프

9
비동기 결과를 잊어 버린 경우 비동기 결과가 끝나고 가비지 수집됩니다. 그러나 코드의 버그로 인해 충돌이 발생하면 그것에 대해 배우지 못할 것입니다. 그 이유입니다.
로마 엘리자 로프

77

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


11

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입니다 연속 , funs입니다 반면 async동시
Akbolat SSS

모든 작업에 대해 한 번 실행을 사용하고 각 작업에 대해 비동기를 사용했습니다. 각각 다른 코 루틴에서 시작되어 누군가를 기다리지 않기 때문에 더 빠를 수 있습니까? 이것은 잘못된 비교입니다. 일반적으로 성능은 같습니다. 한 가지 중요한 차이점은 시작시 항상 소유자를 분리하는 비동기 대신 새로운 코 루틴을 시작한다는 것입니다. 또 다른 요인은 비동기 작업 중 하나가 어떤 이유로 실패하면 부모 코 루틴도 실패한다는 것입니다. 이것이 비동기가 출시만큼 인기가없는 이유입니다.
p2lem8dev

1
이 답변은 비동기 대신 일시 중단 기능을 시작 대신 직접 비교하는 것이 맞지 않습니다. 예제에서 직접 suspend 함수를 호출하는 대신 launch (Dispatchers.IO) {downloadTask1 ()}을 호출하면 둘 다 동시에 순차적으로 실행되지 않고 출력을 얻을 수는 없지만 출력을 얻을 수는 없음을 알 수 있습니다 순차적이지 않습니다. 또한 deferred.await ()를 연결하지 않고 deferred.await ()를 별도로 호출하면 비동기가 순차적임을 알 수 있습니다.
Thracian

2
-1 이것은 명백한 잘못입니다. 모두 launchasync새로운 코 루틴을 시작합니다. 자녀가없는 단일 코 루틴을 3 명의 자녀가있는 단일 코 루틴과 비교합니다. 각 async호출을 대체 launch할 수 있으며 동시성과 관련하여 전혀 변경되지 않습니다.
모이라

이 답변에서 불필요한 소음은 공동 루틴 주제 외부에 복잡성을 추가하는 것입니다.
truthadjustr

6
  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


1
이 의견에 감사드립니다. 스레드의 모든 지점을 수집했습니다. 모든 출시가 취소되는 것은 아닙니다. 예를 들어 Atomic을 취소 할 수는 없습니다.
p2lem8dev

4

발사 는 직업을 반환

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

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

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

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

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


0

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

발사 / 비동기 결과 없음

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

결과에 대한 비동기

  • 결과를 기다려야하고 효율성을 위해 병렬로 실행할 수있는 경우
  • 호출되는 코드를 차단
  • 병렬로 실행
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.