2020-02-07 TIL

할 일 목록

  1. 코드스피츠 나머지 정리
  2. 코틀린 스터디 내용 다시 듣기
  3. 알고리즘 문제 2문제 풀이
  4. 코틀린 코드 쳐보기.

완료하지 못한 목록

  1. 코틀린 코드 쳐보기.

완료 목록

  1. 코드스피츠 나머지 정리
  2. 코틀린 스터디 내용 다시 듣기
  3. 알고리즘 문제 2문제 풀이

5Fs

1. Fact

1. 네트워크

  • 서브넷 구성과 IP 주소 할당

    1. 일반적으로는 동일한 부서를 서브넷 하나로 묶는다.
    2. 서브넷 분할 방침이 정해지면 각 서브넷에 네트워크 주소를 할당한다. 보통 사설 IP를 할당한다.
  • 하드웨어와 소프트웨어의 선택

    1. 클라우드
    2. 온프레미스
  • 인터넷과의 접속

    1. 광회선이 주류가 된 현재 인터넷을 이용하는 데는 액세스 회선 서비스와 ISP(Internet Service Provider)두가지를 조합해서 사용하는 것이 기본적이다.
    2. 액세스 회선이란 가장 가까운 전화국에서 사무실이나 가정까지 연결되는 광회선이다.
  • 서버의 공개

    1. 조직의 건물 내에 서버를 두는 경우 안전한 공조와 전원을 확보할 수 있는 공간에 서버를 둠과 동시에 방화벽에 DMZ 설정해서 여기에 서버를 연결시킨다.
    2. 공개하는 서버는 외부에서 접속할 수 있어야 하기 때문에 고정된 전역 IP 주소가할당된다.
  • 네트워크의 다중화

    1. 브로드캐스트 스톰

      • LAN의 접속 라우터를 여러 개 만들면 브로드새크트 프레임이 LAN 내에서 무한히 도는 현상.
      • 위의 문제를 해결하기 위해 스패닝 트리 프로토콜을 사용한다.

2. 코틀린

  • 밑의 코드를 보면 main을 Coroutine$main.doResume이라는 함수를 자동으로 만들어 내는데 자동으로 만들어내는걸 보면 do 하면서 본인의 state_0 본인이 continuation임 println(a)하고 그다음 result 안에 task를 넣어줌. task 가 continuation this를 받아감. 이 task가 이때 컨티뉴에이션 객체를 이때 받는거랑 마찬가지임 . 우리가 만든 테스크가 어떻게 continuation 객체를 받냐고 시스템이 만든 continuation 객체가 여기에 온것임. suspend 함수가 이렇게 나누는 것임.
  • 실제로는 두덩어리임 println(‘a’)까지는 suspend coroutine 부분이 아니라 println(tast{it(‘b’)}) 까지가 한덩어리고
  • suspend coroutine을 기준으로 println(“C”)만 한덩어리고
  • 위의 두개가 실행되고 전달되서 마지막이 실행됨
  • 자동으로 resume을 때리면 밑의 doResume으로 들어오게됨 다시
suspsend fun <T>task:(block:((T)->Unit)->Unit):T = suspsnedCoroutine{
    con:Continuation<T>->block{cont.resume(it)} //얘가 b가 됨
}

suspend fun main(){
    println("a")
    println(task{it("b")})  // ((T->Unit)->Unit 여기의 it은 (T->Unit)에 해당
    println("c")
}

Coroutine$main.prototype.doResume = function() {
    do
    try {
        switch (this.state_0) {
            case 0:
            println('a');
            this.state_0 = 2;
            this.result_0 = task(main$lamda, this);
            if (this.result_0 === COROUTINE_SUSPENDED)
              return COROUTINE_SUSPENDED;
            continue;
            case 1:
              throw this.exception_0;
            case 2:
              println(this.result_0);
              println('c');
              return;
            default:this.state_0 = 1;
              throw new Error("State Machine Unreachable execution");
        }
    }}
    while(true);
  • 우리는 suspend coroutine에서 continuation가 이 로직의 바로 다음번으로 전개될 수 있는 방법이란걸 알고 있으니 타임아웃을 만들어 볼 수 있다.
  • time아웃 원리는 원하는 시간을 t로 받고 리턴을 할 필요가 없음.
  • 쟤는 task함수를 이용해서 suspendCoroutine에 진입 한것임
  • task는 window.setTimeout에 t만큼 돈 다음 실행되는 람다 안에서 받아온 it (위 코드의 cont.resume(it))에 Unit을 줬음.
  • 그래서 밑에 애는 시간만 끌고 resume을 해준것이다.
  • a가 출력되서 windowSetTimeout 1초가 지난 후에나 b가 출력됨
  • suspend의 resume이 저때 발생하기 때문이다.
  • timeout은 리턴값이 없지만 window.setTimeout을 끌어주는 효과가 생김
  • 밑의 코드를 보면 async non-blocking 함수를 sync blocking 형태로 사용할 수 있게 되었음.
  • a와 b가 동기라면 바로 나와야 하는데 중간에 비동기이면서 async 스타일을 사용하고 있는 setTimeout이 껴들어 있는데도 불구하고 sync blocking 스타일로 쓸 수 있게 됬다.
  • 밑과 같이 써서 a 다음 비동기 적으로 1초가 지나고 b가 호출 되었음.
  • 여기 1초는 동기형으로 blocking 하고 있지 않음 . 여기서 유아이 다 움직인다.
  • timeout이 블로킹 하고 있지 않는데 밑의 코드는 순서대로 실행이 된다.
  • 코루틴은 언어의 특정 문법을 사용하면 컴파일러가 스위치 컨티뉴에이션으로 바꿔주는 행위이다 .
suspend fun timeout(t:Int):Unit = task{window.setTimeout({it(Unit)}, t)}

suspend fun main() {
    println("a")
    timeout(1000)
    println("b")
}
  • suspendCancellableCoroutine

    1. suspsnedCoroutine와 다르게 예외처리를 할 수 있음.
    2. 밑의 차이는 resumeWithException 를 사용할 수 있다는 것임
    3. 우리는 suspendCoroutine 섹션에서 우리가 원하는 시점에 continuation resume을 때릴 수 있다는 사실을 알고 있다.
    4. 그러면 continuation resume말고 promise 면 reject일때 어떻게 할건가? resumeWithException으로 처리하면 된다.
    5. continuation객체에 일반적인 레포트를 받아주는 resume과 예외적인 레포트를 받아주는 resumeWithException이 있다.
    6. 어떻게 window.fetch(Request(“a.txt)).await()는 response이다. 왜냐면 window.fetch(Request(“a.txt))가 Promise<response>를 리턴할 것이고, await의 리턴값을 보면 T를 리턴하기 때문에 response를 리턴한다.
    7. reponse1.text()하면 String을 반환하는 promise 객체가 나올 것임. await 을 하면 string 나올것이고 .
    8. javascipt와 굉장히 유사하다 . async라는 키워드 대신에 suspend를 썼고 await라는 언어 수준의 여기에 있는 수신함수를 치환한것 뿐이다.
    9. 다른 함수는 continuation의 resume에 접근할 수 없게 만든다. 코틀린은 그와 다르게 suspendCoroutine만 부르면 coroutine iterator 섹션으로 바꿔준다. 그리고 니가 맘대로 하게 해주고 resume 또한 마음대로 할 수 있음 .
    10. cps를 이해하고 cps가 실제적으로 섹션구분으로 continuation 섹션을 만들어내는 과정도 이해하고 얘의 실행기도 이해하면 코루틴이 쉽다.
suspend fun <T>task((block:(T)->Unit)->Unit): T = suspendCoroutine{
    cont:Continuation<T>->block{cont.resume(it)}
}

suspend fun <T> Promise<T>.await(): T = suspendCancellableCoroutine {
    cont: CancellableContinuation<T> ->
    this.then(
        onFulfilled = {cont.resume(it)}
        onRejected = {cont.resumeWithException(it)}
    )
}

suspend fun main(){
    val response1 = window.fetch(Request("a.txt")).await()
    val text1 = response1.text().await()
    val response2 = window.fetch(Request(text1)).await()
    val text2 = response2.text().await()
    println(text2)
}
  • launch & async

    1. launch 는 Job을 async는 deffered를 만든다.
    2. 그러면 실제 suspend 안에서 launch나 async로 만들어져 있는 job이 continuation section을 나눠주는건 아니고, job같은 경우 내부에서 starting 모드를 결정할 수 있다.
    3. 인자 값으로 context를 받고 빌더함수가 만드는 job을 어떤 context(container contextContainer는 element 컨테이너이고 job도 element이다.)에 넣을건지에 대한 거다.
    4. 아래서 넣은 EmptyCoroutineContext는 dispatcher도 아무 element도 없을것 같지만 시스템이 인지해서 이게 empty라면 기본 디스패처를 넣어주고 기본 잡을 넣어준다.
    5. 인자값 start는 DEFAULT일 경우에 런치로 만들어진 즉시 start를 때림
    6. job은 리턴값이 없음 안에 있는 블록을 무조건 실행한다. 그럼 안에 있는 블록도 suspended 블록이기 때문에 다시 suspend 요소들을 가져올 수 있다. 여기에 동기화 로직만 들어가는게 아니다 suspend안에 suspend가 있으면 switch문 안에 어떤 case에 들어가는 switch문이 생긴다. 왜냐면 하나의 suspend section은 계속해서 do Resume에서 봤던것 처럼 switch문으로 바뀌기 때문 switch 문에 또 switch문이 생길 것임.
suspend fun main() {
    GlobalScope.launch{
        context = EmptyCoroutineContext,
        start = CoroutineStart.DEFAULT
        ){
            timeout(1000)
            println("a")
        }
    }
}
  • 두번째엔 context에 Dispatchers.Default를 넣었는데 dispatcher는 element이긴 한데 coroutine context는 아니었음. 그걸 launch에 넘겨주면 얘를 담고 있는 빈 context를 만들어줌. 그냥 dispatcher만 넣으면 이것만 넣어서 컨텍스트를 만들어준다. 이 디스패처에 따라 어떤 실행기가 이터레이션을 처리할지가 결정이 된다. 실행기가 a스레드와 b스레드에 실행되는게 있다면 어떤 디스패처를 고르냐에 따라 어떤 스레드에서 처리될지 결정된다. 스레드 환경에서는 보통 디스패처가 어떤 스레드를 쓰냐가 중요해지기 때문에 스레드를 고르는 행위나 코루틴 풀에서 작동시킬 거냐 에 따라 달라진다.dispatcher가 스레드를 결정하기 때문에 중요하다.
suspend fun main() {
    GlobalScope.launch{
        context = EmptyCoroutineContext,
        start = CoroutineStart.DEFAULT
        ){
            timeout(1000)
            println("a")
        }
    }//join()을 걸면 위에것이 끝나야 밑의것이 시작됨.

    GlobalScope.launch{
        context = Dispatchers.Default,
        start = CoroutineStart.DEFAULT  //.LAZY를 할경우
        ){
            timeout(1000)
            println("a")
        }
    }// .start()를 해줘야함.
}
  • 그래서 job의 특징

    1. 안에서 suspend 구문을 지원하긴 하지만 무조건 위에서 아래로 흐르는 로직 이외는 지원하지 않는다.
    2. 리턴값이 없고 안이 무조건 실행된다. 그래서 launch 이다. 그냥 쏘면 걍 안이 실행될 뿐 더이상 할 수 있는게 없다. 얘는 continuation resume을 쓸수가 없음. 코틀린 코루틴 생태계에서 job이나 deffered를 쓰면 문제점은 우리가 컨티뉴에이션의 리쥼에 대한 통제권을 잃는다.그래서 블록 안에 동기적으로 실행되는것밖엔 안된다. 블록 안에서 ajax 콜백에서 리쥼같은건 못함 걍 흘러내림
suspend fun main() {
    val a = GlobalScope.async{
        context = EmptyCoroutineContext,
        start = CoroutineStart.DEFAULT
        ){
            timeout(1000)
            println("a")
        }
    }//join()을 걸면 위에것이 끝나야 밑의것이 시작됨.
    val b = GlobalScope.async{
        context = Dispatchers.Default,
        start = CoroutineStart.DEFAULT  
        ){
            timeout(1000)
            println("a")
        }
    }
    println(a.await())
    println(b.await())
}
  • async

    1. asyncDml deffered 객체의 특징은 결과값이 나온다.
    2. 단 async 블록이 만든건 deffered객체이지 값이 아니다
    3. 미래의 a를 반환할 deffered객체가 위에 a에 잡힐것임
    4. deffered특징은 await을 통해 일정시점이 지난 다음 continuation(iteration) 때 우리는 async의 결과값을 받을 수 있다.
    5. start 인자가 default라 두 블록이 실행되자마자 실행이 되버림 그래서 밑에 동시에 값이 나옴.
    6. launch와 다른점이 외부세계와 대화를 할 수 있다.블록 내부의 값이 외부에 노출이 가능하다. deferred의 장점은 deffered안의 값을 리턴할 수 있다.
    7. deffered await을 통해 블록내부의 값이 해소됬을때 그 값을 외부에 노출할 수 있다.
    8. 만약 b의 start 인자가 lazy라면 언제 start 될까? b.await할때 시작된다.
    9. 아니면 b.start를 밑에서 한다면 동시에 나온다.
    10. job이나 deffered의 특징은 내부에서 low 레벨의 continuatino을 만나지 않는다. 그리고 중간에 resumeWithException을 가질 수 있다.
    11. 하지만 우리한태 언제 resume을 호출할지에 대한 권한은 없어진다.
    12. resume을 우리가 원할때 호출하고 싶다면 suspendCoroutine 시스템 함수를 이용해서 섹션을 만들어내는 수밖에 없다.얘만이 컨티뉴에이션을 준다.
fun <T>async(block: suspend CoroutineScope.() -> T) = GlobalScope.async{block()}
fun lauch(block: suspend CoroutineScope.() -> Unit) = GlobalScope.launch{block()}
  • suspendCoroutine을 사용하는 블록을 task로 추상화를 위에서 시켰었는데 그럼 나와있는 빌더를 이용한 글로벌 스코프 async나 global scope launch도 위와같이 추상화 시킬 수 있음.
  • async를 보면 suspend CoroutineScope를 수신함수로 받는 인자를 받아서 오른쪽에 직접 블록을 넘겨주면 된다.
  • async launch task를 이용하면 손쉽게 우리가 원하는 yield를 정리해서 끼워넣을 수 있음. 그럼 위에서 아래로 읽으면 비동기로 쳐리되지만 sync blocking 으로 보인다.
  • 다른 비동기 로직들은 callback을 이용하는데 이것의 나쁜점은 어휘공간이 보존되지 않는다는 건데 콜백 안에서 인자로 받지 않으면 인자로 가져올 수 없음.
  • suspend함수를 쓰면 여기 만들어진 지역 변수가 이 모든 비동기로직이 한꺼번에 공유되기 때문에 손쉽게 클로저에 도움받지 않고 동기로직처럼 짤 수 있다.

2.Feelings

  • 코틀린 코루틴에 대해서 정말 4일 전까지만 해도 영상을 듣거나 책을 읽어도 이해를 하지 못했는데, 영상을 여러번 돌려보고 하나하나 무슨말을 하는지 따져가면서 반복하다 보니 눈에 들어오기 시작했다.
  • 오늘 알고리즘 문제를 풀면서 수학문제를 만났는데, 정말 알고리즘 문제를 다양하게 많이 풀어서 어떤 유형의 수학문제들이 나오는지 익히고 수학 공부가 필요함을 느꼈다.

3.Findings

  • java의 future는 isCompleted를 while문으로 사용한다면 sync nonblocking이고 future.get으로 사용한다면 block async 이다. 전자는 future.isCompleted를 계속해서 요청해서, 백그라운드 스레드가 처리됬는지 확인하고, future.get 은 callback과 비슷한 놈이다. async로 요청을 하지만, 실행한 함수를 블로킹 걸고 끝나면 제어권을 실행함수로 넘겨주면서 콜백으로 get에 원하는 값이 존재하도록 하기 때문이다.
  • 오늘은 sync async block non-blocking 에 대해서 더 공부를 할 수 있었다.
  • java의 completedFuture는 async nonblocking 방식이다. 자바스크립트의 promise와 유사한데 반제어 상태로 async non-blocking 방식을 구현한다. completedFuture를 사용하면 백그라운드는 실행되고 제어권도 실행한 함수로 돌아온다. 그래서 뒤에 코드를 계속해서 작성할 수 있다. 그리고 이후에 completedFuture에 접근을 할 경우, 백그라운드 실행이 완료 됬다면, 값을 반환하고, 완료되지 않았다면, 그 자리에서 기다리는 방식이다.
  • kotlin 에서 operator로 getValue와 setValue를 넣어준다면 그것은 by로 delegate 한것과 동일하다.
  • by로 deligate를 할 경우에, 그냥 a.b를 부를경우 b를 getValue로 가져오고 a.b = 1 을 할 경우 setValue로 값을 설정한다. 그래서 이 두 오퍼레이터에 커스텀을 해서 처리할 수가 있다.

4.Future Action Plan

  • java future가 실제로 async block 인지 코드로 뜯어봐야겠따.
  • kotlin delegate에서 제공하는 몇가지를 구현해봐야겠다.

5.FeedBack


Written by@Zero1
This blog is for that I organize what I study and my thinking, feeling and experience.

GitHub