복수 인스턴스 리턴

  • Kotlin의 복수 데이터 리턴 방법
    • Pair를 활용해서 두 개의 인스턴스 리턴
		var chicken = Chicken()
    var eggs = chicken.getEggs()
    var listEggs = eggs.toList()
    
//    first, second로 관리
//    var firstEgg = eggs.first
//    var secondEgg = eggs.second
    
    // 리스트로 관리
    var firstEgg = listEggs[0]
    var secondEgg = listEggs[1]

    println("달걀의 종류는 ${eggs} 입니다.")
    println("리스트 달걀의 종류는 ${listEggs} 입니다.")
    println("첫번째 달걀의 종류는 ${firstEgg} 입니다.")
    println("두번째 달걀의 종류는 ${secondEgg} 입니다.")
}

class Chicken {
    fun getEggs(): Pair<String, String> {
        var eggs = Pair("달걀", "맥반석")
        return eggs
    }
}
  • Triple를 활용해서 세 개의 인스턴스 리턴
fun main() {
    var chicken = Chicken()
    var eggs = chicken.getThreeEggs()
    var listEggs = eggs.toList()
    
//    first, second, third로 관리
//    var firstEgg = eggs.first
//    var secondEgg = eggs.second
//    var eggTime = eggs.third
    
    // 리스트로 관리
    var firstEgg = listEggs[0]
    var secondEgg = listEggs[1]
    var eggTime = listEggs[2]

    println("달걀의 정보는 ${eggs} 입니다.")
    println("리스트 달걀의 정보는 ${listEggs} 입니다.")
    println("첫번째 달걀의 종류는 ${firstEgg} 입니다.")
    println("두번째 달걀의 종류는 ${secondEgg} 입니다.")
    println("달걀은 ${eggTime}에 나왔습니다.")
}

class Chicken {
    fun getTwoEggs(): Pair<String, String> {
        var eggs = Pair("달걀", "맥반석")
        return eggs
    }

    fun getThreeEggs(): Triple<String, String, Int> {
        var eggs = Triple("달걀", "맥반석", 20230101)
        return eggs
    }
}

자기 자신의 객체를 전달

  • 설명
    • 코틀린에서는 Scope Functions들을 제공해요
    • 객체를 사용할때 임시로 Scope를 만들어서 편리한 코드 작성을 도와줘요
  • Kotlin의 Scope Function 종류
    • let function의 활용
      • 중괄호 블록안에 it으로 자신의 객체를 전달하고 수행된 결과를 반환해요
		var strNum = "10"

    var result = strNum?.let {
        // 중괄호 안에서는 it으로 활용함
        Integer.parseInt(it)
    }

    println(result!!+1)
  • with function의 활용
    • 중괄호 블록안에 this로 자신의 객체를 전달하고 코드를 수행해요
    • this는 생략해서 사용할 수 있으므로 반드시 null이 아닐때만 사용하는게 좋아요
		var alphabets = "abcd"

    with(alphabets) {
//      var result = this.subSequence(0,2)
        var result = subSequence(0,2)
        println(result)
    }
  • also function의 활용
    • 중괄호 블록안에 it으로 자신의 객체를 전달하고 객체를 반환해줘요
    • apply와 함께 자주 사용해요
fun main() {
    var student = Student("참새", 10)

    var result = student?.also {
        it.age = 50
    }
    result?.displayInfo()
    student.displayInfo()
}

class Student(name: String, age: Int) {
    var name: String
    var age: Int

    init {
        this.name = name
        this.age = age
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

확장함수

fun main() {
    fun Student.getGrade() = println("학생의 등급은 ${this.grade} 입니다")
    var student = Student("참새", 10, "A+")
    student.displayInfo()
    student.getGrade()
}

class Student(name: String, age: Int, grade: String) {
    var name: String
    var age: Int
		var grade: String

    init {
        this.name = name
        this.age = age
				this.grade = grade
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

getGrade() -> 확장함수

if(a in b){}
if(a is b){}

쓰레드

  • 설명
    - 프로그램은 하나의 메인 쓰레드(실행흐름)가 존재해요
    - 하나의 메인 쓰레드는 —→ fun main() ←— 메인함수를 의미해요
    - 실습 프로그램은 메인 쓰레드위에서 로직을 실행해서 동시처리가 불가능했어요
    - 별도의 자식 쓰레드를 생성해서 동시에 로직을 실행할 수 있어요
    - 코틀린은 thread 키워드로 쓰레드를 생성할 수 있어요

프로세스 바탕화면에 더블클릭해서 실행을한다 -> 프로세스 실행됨

동시에 실행한다 -> 하나의 cpu자원 경쟁

코틀린에서 코루틴 쓸땐 외부 라이브러리 모듈 추가해야됨

코루틴

하드웨어 자원의 효율적인 할당을 가능하게함

쓰레드보다 더욱 가볍게 사용 구글에서 적극 권장

  • 빌더와 함께 사용
    • 일반적으로 launchasync빌더를 가장 많이 사용함
    • launch는 결과값이 없는 코루틴 빌더
    • async는 결과값이 있는 코루틴
    • 코루틴은 스코프로 범위를 지정함
      GlobalScope: 앱이 실행된 이후 계속 수행되어야 할때
      CoroutineScope: 필요할때만 생성, 사용 후 정리
    • 코루틴을 실행할 쓰레드를 Dispatcher로 지정가능

      왜? 쓰레드보다 코루틴이 더 경량화된 개념인데
      하나의 쓰레드 안에서 여러개 코루틴을 작동하기위해

      Dispatchers.Main : UI와 상호작용하기 위한 메인쓰레드
      Dispatchers.IO : 네트워크나 디스크 I/O작업에 최적화 되어있는 쓰레드
      Dispatchers.Default : 기본적으로 CPU최적화 되어있는 쓰레드

안드로이드에서는 특히 Dispatcher간의 변환을 해야하는 작업을 고려해야해요

코루틴의 정의

fun main(args: Array<String>) {
    println("메인쓰레드 시작")
    var job = GlobalScope.launch {
        delay(3000)
        println("여기는 코루틴...")
    }
    println("메인쓰레드 종료")
}

안드로이드는 앱이 항상켜져 있어서 사용가능

JVM에선 메인쓰레드가 종료되니 결과 못보긴함

fun main(args: Array<String>) {
    println("메인쓰레드 시작")
    var job = GlobalScope.launch {
        delay(3000)
        println("여기는 코루틴...")
    }
    runBlocking {
        job.join()
    }
    println("메인쓰레드 종료")
}

join을 쓰면 job코루틴 끝날때까지 기다려야함

fun main(args: Array<String>) {
    println("메인쓰레드 시작")
    var job = CoroutineScope(Dispatchers.Default).launch {
        delay(3000)
        println("여기는 코루틴...")
    }
    runBlocking {
        job.join()
    }
    println("메인쓰레드 종료")
		job.cancel() //코루틴 스코프라 사용후 정리
}
		println("메인쓰레드 시작")
    var job = CoroutineScope(Dispatchers.Default).launch {
        var fileDownloadCoroutine = async(Dispatchers.IO) {
            delay(10000)
            "파일 다운로드 완료"
        }
        var databaseConnectCoroutine = async(Dispatchers.IO) {
            delay(5000)
            "데이터베이스 연결 완료"
        }
        println("${fileDownloadCoroutine.await()}")
        println("${databaseConnectCoroutine.await()}")
    }
    runBlocking {
        job.join()
    }
    println("메인쓰레드 종료")
    job.cancel()

데이터 가져오고 넣는행위하기위해 적합한 Dispatchers.IO

결과값을 리턴받아야하기 때문에 await은 일시중단이 가능한 코루틴에서 실행가능

코루틴과 쓰레드

💡 동시성 프로그래밍을 다시 정리할게요

heap 메모리

  • 설명
    • 쓰레드와 코루틴은 둘 다 동시성 프로그래밍을 위한 기술이예요
    • 동시성 프로그래밍 기술은 컨텍스트 스위칭이 중요한 개념이예요

💡 쓰레드와 코루틴의 차이를 정리할게요

  • 쓰레드

    • 작업 하나하나의 단위 : Thread

      • 각 Thread 가 독립적인 Stack 메모리 영역을 가져요
    • 동시성 보장 수단 : Context Switching
      - 운영체제 커널에 의한 Context Switching 을 통해 동시성을 보장해요
      - 블로킹 (Blocking)
      - Thread A가 Thread B 의 결과를 기다리고 있어요
      - 이 때, Thread A는 블로킹 상태라고 할 수 있어요
      - A는 Thread B 의 결과가 나올 때 까지 해당 자원을 사용하지 못해요

      예시

    • Thread A가 Task 1을 수행하는 동안 Task 2 의 결과가 필요하면 Thread B를 호출해요

    • 이때 Thread A는 블로킹 되고 Thread B로 프로세스간에 스위칭이 일어나 Task 2을 수행해요

    • Task 2가 완료되면 Thead A로 다시 스위칭해서 결과 값을 Task 1에게 반환해요

    • 이때 Task 3, Task 4는 A, B작업이 진행되는 도중에 멈추지 않고 각각 동시에 실행되게 되요

    • 이때 컴퓨터 운영체제 입장에서는 각 Task를 쪼개서 얼마나 수행할지가 중요하겠죠?

    • 그래서 어떤 쓰레드를 먼저 실행해야할지 결정하는행위를 스케쥴링이라고 해요

    • 이러한 행위를 통해 동시성을 보장해요

  • 코루틴

    • 작업 하나하나의 단위 : Coroutine Object
      • 여러 작업 각각에 Object 를 할당해요
      • Coroutine Object 도 엄연한 객체이기 때문에 JVM Heap 에 적재해요 (코틀린 기준)
    • 동시성 보장 수단 : Programmer Switching (No-Context Switching)
      • 소스 코드를 통해 Switching 시점을 마음대로 정해요 (OS는 관여하지 않아요)

      • Suspend (Non-Blocking)
        - Object 1이 Object 2의 결과를 기다릴 때 Object 1의 상태는 Suspend로 바뀌어요
        - 그래도 Object 1을 수행하던 **Thread는 그대로 유효해요**
        - 그래서 Object 2도 Object 1과 동일한 Thread에서 실행되어요

        예시

    • Coroutine은 작업 단위가 Object라고 했어요
    • Task 1을 수행하다가 Task 2의 수행요청이 발생했다고 가정해볼게요
    • 신기하게도 컨텍스트 스위칭 없이 동일한 Thread A에서 수행할 수 있어요
    • Thread C처럼 하나의 쓰레드에서 여러 Task Object들을 동시에 수행할 수 있어요
    • 이러한 특징때문에 코루틴을 Light-Weight Thread라고 이야기해요
      • 동시처리를 위해 스택영역을 별도로 할당하는 쓰레드처럼 동작하지 않아요
      • 하지만 동시성을 보장할 수 있어요
      • 하나의 쓰레드에서 다수의 코루틴을 수행할 수 있어요
      • 커널의 스케쥴링을 따르는 컨텍스트 스위칭을 수행하지 않아요
  • 요약

    • 쓰레드나 코루틴은 각자의 방법으로 동시성을 보장하는 기술이예요
    • 코루틴은 Thread를 대체하는 기술이 아니예요
    • 하나의 Thread를 더욱 잘개 쪼개서 사용하는 기술이예요
    • 코루틴은 쓰레드보다 CPU 자원을 절약하기 때문에 Light-Weight Thread라고 해요
    • 구글에서는 코틀린의 코루틴 사용을 적극 권장하고 있어요

0개의 댓글