반응형
05-14 05:47
Today
Total
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
관리 메뉴

개발하는 고라니

Blocking vs Non-Blocking (with, Sync vs Async) 본문

Programming

Blocking vs Non-Blocking (with, Sync vs Async)

조용한고라니 2021. 9. 27. 02:07
반응형

프로그래밍을 하다보면 동기와 비동기는 많이 들어보았을 것이다.

이때 동기는 흔히

'A가 B에게 작업을 요청했을 때, B의 작업이 끝날 때 까지 A가 기다렸다가 나머지 작업을 수행하는 것.'

 

반대로 비동기는 

'A가 B에게 작업을 요청했을 때, B의 작업과 별개로 A의 나머지 작업을 다시 수행하는 것.'

 

으로 이해하고 있었다. 하지만 블로킹과 논블로킹의 개념을 접하고 나니 알고있던 것이 반은 맞고 반은 틀린(?) (사실 반이라도 맞는지 모르겠다) 사실이었다. 그래서 쉽사리 알기 힘든 개념이고, 앞으로 프로그래밍 하는데 있어 반드시 필요한 개념이라고 생각되어 동기와 비동기, 블로킹과 논블로킹의 개념을 정리해보고자 한다.

Sync vs Async, Blocking vs Non-Blocking

※ 블로킹과 논블로킹은 "제어권"에 관한 이야기이고,

동기와 비동기는 그 제어권을 반환하는 "시간(타이밍)"에 관한 이야기임을 간단하게 기억하고 있자.

 

먼저, [동기/비동기]는 작업을 수행하는 주체가 2개 이상이어야 한다. 이 때 작업의 시간(시작, 종료 등)을 서로 맞춘다"동기"라고 부르고, 서로 작업의 시간이 관계없다면 이를 "비동기"라고 부른다.

 

[블로킹/논블로킹]은 작업의 대상이 2개 이상이어야 한다. 두 개념이 서로 바라보는 관점이 다르기 때문에 블로킹/동기, 논블로킹/동기, 블로킹/비동기, 논블로킹/비동기의 4가지 조합이 가능하다.

 

시간이 없다면 간단하게 한줄 요약만 읽고 가면 되겠다.

논블로킹 : 호출된 함수(B)가 자신을 호출한 함수(A)에게 다른 일을 할 수 있도록 하면(제어권을 넘겨주면) 논블로킹
블로킹 : 호출된 함수(B)가 자신의 일이 끝날 때까지 A가 아무 일도 못하게 막으면 블로킹
---------
비동기 : 호출된 함수(B)의 작업 처리 결과를 호출한 함수(A)가 관심 없어하면 비동기
동기 : 호출된 함수(B)의 작업 처리 결과를 호출한 함수(A)가 관심 있으면 동기 (결과를 받을 때 까지 계속 Polling or Waiting)

Sync vs Async

Synchronous : 작업을 동시에 수행하거나, 동시에 끝나거나, 끝나는 동시에 시작함을 의미.

조금 다른 말로는, 호출한 함수가 결과 값을 전달하는 시간과 제어권을 반환하는 시간이 같을 때

 

Asynchronous : 시작, 종료가 일치하지 않으며, 끝나는 동시에 시작을 하지 않음을 의미.

 

동기 작업이란 작업을 수행하는 두 개 이상의 주체가

1) 서로 동시에 수행하거나

2) 동시에 끝나거나

3) 끝나는 동시에 시작할 때

를 의미한다. 즉, 시작과 종료를 동시에 하거나, 하나의 작업이 끝나는 동시에 다른 주체가 작업을 시작하면 "동기" 작업이다.

 

비동기 작업은 작업을 수행하는 두 개 이상의 주체가

1) 서로의 시작, 종료 시간과 관계 없이 별도의 수행 시작, 종료 시간을 갖을 때를 의미한다.

서로 다른 주체가 하는 작업이 자신의 작업 시작, 종료 시간과 관계없을 때 "비동기" 작업이다.

출처: https://deveric.tistory.com/99

Blocking vs Non-Blocking

Blocking : 자신의 작업을 진행하다가 다른 주체의 작업이 시작되면 다른 작업이 끝날 때까지 기다렸다가 자신의 작업을 시작하는 것.

조금 다른 말로, 제어권을 호출한 함수가 들고있다가 작업을 마치면 제어권을 반환하는 것

 

Non-Blocking : 다른 주체의 작업에 관련없이 자신의 작업을 하는 것

 

블로킹 작업은 A가 자신의 작업을 하다가 다른 작업 주체(B)가 하는 작업의 시작부터 끝까지 기다렸다가 다시 자신(A)의 작업을 시작하는 것을 의미한다.

 

반대로 다른 주체(B)의 작업과 관계없이 자신(A)의 작업을 계속한다면 이를 논블로킹이라고 할 수 있다.

출처: https://deveric.tistory.com/99

 

"Blocking vs Non-Blocking"작업을 수행하기 위한 '제어권'이 어느 작업의 주체에게 있는지에 관한 관점

 

"Sync vs Async"작업의 순서와 결과(처리)의 관점이라고 생각한다. 즉 결과를 돌려주었을 때 순서와 결과에 관심이 있는지 없는지로 판단할 수 있다.

Java로 보는 동기와 비동기, 블로킹과 논블로킹 예제

코드와 콘솔에 출력되는 값을 보며 이해하면 조금 더 잘되려나 싶어서 진행해보았다. 팀장2명의 사원에게 업무 지시를 하는 상황을 가정해보았다.

블로킹, 동기

    @Test
    public void 블로킹_동기() throws Exception{
        //1
        int time = 0;

        //2
        System.out.println("팀장이 사원 1에게 업무를 지시한다.(제어권을 넘긴다) : " + LocalDateTime.now().format(formatter));
        System.out.println("결과 : " + 동기_사원1());

        System.out.println("\n팀장이 사원 2에게 업무를 지시한다.(제어권을 넘긴다) : " + LocalDateTime.now().format(formatter));
        System.out.println("결과 : " + 동기_사원2() + "\n");

        while(time < 4){
            Thread.sleep(1000);
            System.out.println("팀장이 할 일을 한다. : " + LocalDateTime.now().format(formatter));
            time++;
        }
        
        //3
        System.out.println("(End) 팀장이 할 일을 마쳤다. : " + LocalDateTime.now().format(formatter));

    }
    
    String 동기_사원1() throws InterruptedException{

        Thread.sleep(3000);
        System.out.println("사원 1이 업무를 마쳤다. (제어권과 결과값을 동시에 반환한다) : " + LocalDateTime.now().format(formatter));

        return "사원 1의 결과";
    }
    String 동기_사원2() throws InterruptedException{

        Thread.sleep(5000);
        System.out.println("사원 2이 업무를 마쳤다. (제어권과 결과값을 동시에 반환한다) : " + LocalDateTime.now().format(formatter));

        return "사원 2의 결과";
    }
팀장이 사원 1에게 업무를 지시한다.(제어권을 넘긴다) : 2021-09-27 01시 05분 10초
사원 1이 업무를 마쳤다. (제어권과 결과값을 동시에 반환한다) : 2021-09-27 01시 05분 13초
결과 : 사원 1의 결과

팀장이 사원 2에게 업무를 지시한다.(제어권을 넘긴다) : 2021-09-27 01시 05분 13초
사원 2이 업무를 마쳤다. (제어권과 결과값을 동시에 반환한다) : 2021-09-27 01시 05분 18초
결과 : 사원 2의 결과

팀장이 할 일을 한다. : 2021-09-27 01시 05분 19초
팀장이 할 일을 한다. : 2021-09-27 01시 05분 20초
팀장이 할 일을 한다. : 2021-09-27 01시 05분 21초
팀장이 할 일을 한다. : 2021-09-27 01시 05분 22초
(End) 팀장이 할 일을 마쳤다. : 2021-09-27 01시 05분 22초

 

1) 팀장이 사원 1에게 업무 지시를 했고, 완료되길 기다리고 있다.

2) 사원 1은 3초동안 지시받은 업무를 완료했다.

3) 팀장은 사원 1의 결과를 받고, 곧이어 사원 2에게 업무 지시를 했다. 마찬가지로 완료되길 기다린다.

4) 사원 2는 5초동안 지시받은 업무를 완료했다.

5) 팀장은 사원 2의 결과를 받아 처리했다.

6) 팀장은 이제 본인의 할일을 4초동안 한다.

논블로킹, 비동기

    @Test
    public void 논블로킹_비동기() throws Exception{
        //1
        int time = 0;

        //2
        System.out.println("팀장이 사원 1에게 업무를 지시한다.(제어권을 넘긴다) : " + LocalDateTime.now().format(formatter));
        비동기_사원1();

        System.out.println("팀장이 사원 2에게 업무를 지시한다.(제어권을 넘긴다) : " + LocalDateTime.now().format(formatter));
        비동기_사원2();

        while(time < 4){
            Thread.sleep(1000);
            System.out.println("팀장이 할 일을 한다. : " + LocalDateTime.now().format(formatter));
            time++;
        }
        //3
        System.out.println("(End) 팀장이 할 일을 마쳤다. : " + LocalDateTime.now().format(formatter));

    }
    void 비동기_사원1(){
        System.out.println("# 사원 1이 업무 지시를 받고 진행한다. (제어권을 반환한다)");

        Executors.newSingleThreadExecutor().submit(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("# 사원 1이 업무를 마쳤다고 카톡을 보낸다. : " + LocalDateTime.now().format(formatter));
        });
    }
    void 비동기_사원2(){
        System.out.println("# 사원 2이 업무 지시를 받고 진행한다. (제어권을 반환한다)");

        Executors.newSingleThreadExecutor().submit(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("# 사원 2이 업무를 마쳤다고 카톡을 보낸다. : " + LocalDateTime.now().format(formatter));
        });
    }
팀장이 사원 1에게 업무를 지시한다.(제어권을 넘긴다) : 2021-09-27 01시 32분 45초
# 사원 1이 업무 지시를 받고 진행한다. (제어권을 반환한다)

팀장이 사원 2에게 업무를 지시한다.(제어권을 넘긴다) : 2021-09-27 01시 32분 45초
# 사원 2이 업무 지시를 받고 진행한다. (제어권을 반환한다)

팀장이 할 일을 한다. : 2021-09-27 01시 32분 46초
팀장이 할 일을 한다. : 2021-09-27 01시 32분 47초

# 사원 1이 업무를 마쳤다고 카톡을 보낸다. : 2021-09-27 01시 32분 48초

팀장이 할 일을 한다. : 2021-09-27 01시 32분 48초

# 사원 2이 업무를 마쳤다고 카톡을 보낸다. : 2021-09-27 01시 32분 49초

팀장이 할 일을 한다. : 2021-09-27 01시 32분 49초
(End) 팀장이 할 일을 마쳤다. : 2021-09-27 01시 32분 49초

 

1) 팀장은 사원 1에게 업무를 지시하고 보고하라고 한다.

2) 사원 1은 지시받은 업무를 3초동안 진행한다.

3) 팀장은 사원 2에게 업무를 지시하고 보고하라고 한다.

4) 사원 2는 지시받은 업무를 4초동안 진행한다.

5) 팀장은 자리로 돌아와 본인 할 일을 한다.

6) 사원 1은 업무를 마치고 카톡으로 보낸다.

7) 사원 2는 업무를 마치고 카톡으로 보낸다.

8) 팀장은 본인의 할 일을 마친다.

논블로킹, 동기

    @Test
    public void 논블로킹_동기() throws Exception{
        //1
        System.out.println("팀장이 사원 1에게 업무를 지시한다.(제어권을 넘긴다) : " + LocalDateTime.now().format(formatter));
        Future<String> 사원1 = 동기_사원1_();
        System.out.println("팀장이 사원 2에게 업무를 지시한다.(제어권을 넘긴다) : " + LocalDateTime.now().format(formatter));
        Future<String> 사원2 = 동기_사원2_();

        //2
        while(true){
            if(사원1.isDone()){
                System.out.println("사원 1의 업무결과 : " + 사원1.get());
                break;
            }
            System.out.println("팀장 -> 사원 1 : 다 하셨나요?? : " + LocalDateTime.now().format(formatter));
            Thread.sleep(1000);
        }
        //3
        while(true){
            if(사원2.isDone()){
                System.out.println("사원 2의 업무결과 : " + 사원2.get());
                break;
            }
            System.out.println("팀장 -> 사원 2 : 다 하셨나요?? : " + LocalDateTime.now().format(formatter));
            Thread.sleep(1000);
        }

    }
    
    Future<String> 동기_사원1_(){
        System.out.println("# 사원 1이 업무 지시를 받고 진행한다. (제어권을 반환한다)");
        return Executors.newSingleThreadExecutor().submit(() -> {

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("사원 1 -> 팀장 : 네 다했습니다.");
            return "사원 1이 업무를 마치고 전달한다.";
        });
    }
    Future<String> 동기_사원2_(){
        System.out.println("# 사원 2이 업무 지시를 받고 진행한다. (제어권을 반환한다)");
        return Executors.newSingleThreadExecutor().submit(() -> {

            try {
                Thread.sleep(5000);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("사원 2 -> 팀장 : 네 다했습니다.");
            return "사원 2이 업무를 마치고 전달한다.";
        });
    }
팀장이 사원 1에게 업무를 지시한다.(제어권을 넘긴다) : 2021-09-27 01시 48분 58초
# 사원 1이 업무 지시를 받고 진행한다. (제어권을 반환한다)

팀장이 사원 2에게 업무를 지시한다.(제어권을 넘긴다) : 2021-09-27 01시 48분 58초
# 사원 2이 업무 지시를 받고 진행한다. (제어권을 반환한다)

팀장 -> 사원 1 : 다 하셨나요?? : 2021-09-27 01시 48분 58초
팀장 -> 사원 1 : 다 하셨나요?? : 2021-09-27 01시 48분 59초
팀장 -> 사원 1 : 다 하셨나요?? : 2021-09-27 01시 49분 00초
사원 1 -> 팀장 : 네 다했습니다.

사원 1의 업무결과 : 사원 1이 업무를 마치고 전달한다.

팀장 -> 사원 2 : 다 하셨나요?? : 2021-09-27 01시 49분 01초
팀장 -> 사원 2 : 다 하셨나요?? : 2021-09-27 01시 49분 02초
사원 2 -> 팀장 : 네 다했습니다.

사원 2의 업무결과 : 사원 2이 업무를 마치고 전달한다.

 

1) 팀장은 사원 1에게 업무를 지시한다.

2) 사원 1은 지시받은 업무를 3초동안 진행한다. 팀장은 다른 일을 하러 간다.

3) 팀장은 사원 2에게 업무를 지시한다.

4) 사원 2는 지시받은 업무를 5초동안 진행한다. 팀장은 다른 일을 하러 간다.

5) 팀장은 자리로 돌아왔지만 성질이 급해 1초마다 끝났는지 물어본다.

6) 사원 1은 업무를 마치고 결과를 전달했다.

7) 팀장은 사원 1이 준 결과를 처리하고, 사원 2에게 1초마다 끝났는지 물어본다.

8) 사원 2는 업무를 마치고 결과를 전달했다.

9) 팀장은 사원 2가 준 결과를 처리한다.

블로킹, 비동기

굳이 다루진 않겠다. 동작은 블로킹, 동기와 유사하게 동작할 것으로 생각된다.

조금 차이가 있다면... 사원이 업무 결과를 팀장에게 전달하는 것이 아닌 이메일로 보내는 상황을 가정할 수 있을 것 같다.

 

1) 팀장이 사원 1에게 업무 지시를 했고, 완료되길 기다리고 있다.

2) 사원 1은 3초동안 지시받은 업무를 완료했고, E-mail로 보냈다.

3) 팀장은 사원 2에게 업무 지시를 했다. 마찬가지로 완료되길 기다린다.

4) 사원 2는 5초동안 지시받은 업무를 완료했고, E-mail로 보냈다.

5) 팀장은 이제 본인의 할일을 4초동안 한다.

 

출처: https://velog.io/@wonhee010

 

코드와 출력, 순서를 보며 이해에 조금 더 보탬이 되었으면 좋겠습니다. 종일 공부한 내용이지만, 여러 글을 보며 공부하다 보니 잘못 이해한 부분이 있거나 틀린 부분이 있을 시, 지적 해주시면 감사하겠습니다.

 

# References

https://www.youtube.com/watch?v=IdpkfygWIMk 

https://www.youtube.com/watch?v=oEIoqGd-Sns 

https://siyoon210.tistory.com/147

https://deveric.tistory.com/99

https://jokergt.tistory.com/65

 

반응형
Comments