네트워크 - 동기/비동기 & 블로킹/논블로킹

델리만쥬 디퓨저·2024년 9월 23일

네트워크

목록 보기
8/9

동기(Synchronous)와 비동기(Asynchronous)

동기의 정의와 특징

  • 동기(Synchronous) 는 작업을 순차적으로 수행하는 것을 의미
  • 이전 작업이 완료되어야 다음 작업을 시작
  • 요청한 작업에 대한 완료 여부를 확인하고 나서 다음 작업으로 넘어감

예시

  • A 작업이 완료된 후에 B 작업을 시작함
  • 파일을 읽을 때, 파일을 다 읽을 때까지 기다림

JAVA 예제

// 동기적으로 파일 읽기
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;

public class SyncExample {
    public static void main(String[] args) throws IOException {
        System.out.println("Start");
        byte[] data = Files.readAllBytes(Paths.get("file.txt"));
        System.out.println("File content: " + new String(data));
        System.out.println("End");
    }
}
  • Files.readAllBytes 메서드는 파일을 모두 읽을 때까지 대기함
  • 파일을 다 읽은 후에 다음 코드가 실행됨

비동기의 정의와 특징

  • 비동기(Asynchronous) 는 작업의 완료 여부에 관심 없이 다음 작업을 수행함
  • 여러 작업이 동시에 진행될 수 있음
  • 작업 완료 시 콜백이나 Future 등을 통해 결과를 처리함

JAVA 예제

// 비동기적으로 파일 읽기
import java.nio.file.*;
import java.util.concurrent.*;

public class AsyncExample {
    public static void main(String[] args) {
        System.out.println("Start");

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                byte[] data = Files.readAllBytes(Paths.get("file.txt"));
                System.out.println("File content: " + new String(data));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        System.out.println("End");

        // 메인 스레드가 종료되지 않도록 잠시 대기
        // 실제 비동기 작업에서는 메인 스레드가 다른 작업을 수행함
        try {
            Thread.sleep(2000); // 예시를 위해 잠시 대기
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 파일을 읽는 동안 main 메서드는 다음 코드를 실행함
  • 작업이 완료되면 콜백을 통해 결과를 처리함
  • Thread.sleep은 프로그램이 바로 종료되지 않도록 잠시 대기하는 용도이며, 실제 비동기 프로그그램에서는 메인 스레드가 다른 작업을 수행함

동기와 비동기의 차이점

  • 작업 순서의 보장 여부 : 동기는 순서가 보장되지만, 비동기는 순서가 보장되지 않을 수 있음
  • 작업 완료 여부 확인 : 동기는 작업이 완료될 때까지 기다리지만, 비동기는 기다리지 않고 다음 작업을 수행함

블로킹(Blocking)과 논블로킹(Non-Blocking)

블로킹의 정의와 특징

  • 블로킹(Blocking) 은 현재 진행 중인 작업이 완료될 때까지 다른 작업을 수행하지 못하는 것을 의미
  • 함수 호출 시 제어권이 호출된 함수로 넘어가며, 호출자는 작업이 끝날 때까지 기다림

예시

  • 네트워크 요청을 보낼 때, 응답이 올 때까지 대기함
  • 스레드가 I/O 작업을 수행하는 동안 다른 작업을 하지 못함

JAVA 예제

// 블로킹 방식의 HTTP 요청
import java.io.*;
import java.net.*;

public class BlockingHttpExample {
    public static void main(String[] args) throws Exception {
        System.out.println("Start");

        URL url = new URL("https://example.com");
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("GET");

        // 응답이 올 때까지 대기
        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuilder content = new StringBuilder();
        while ((inputLine = in.readLine()) != null) {
            content.append(inputLine);
        }

        in.close();
        con.disconnect();

        System.out.println("Response: " + content.toString());
        System.out.println("End");
    }
}
  • 응답이 올 때까지 대기하기 때문에 다른 작업을 수행할 수 없음

논블로킹의 정의와 특징

  • 논블로킹(Non-Blocking) 은 현재 작업이 진행되는 동안에도 다른 작업을 수행할 수 있는 것을 의미
  • 함수 호출 시 제어권이 즉시 반환되어 호출자는 다른 작업을 계속 수행함

예시

  • 네트워크 요청을 보내고, 응답을 기다리지 않고 다른 작업을 수행함
  • 콜백이나 Future를 통해 결과를 처리함

JAVA 예제

// 논블로킹 방식의 HTTP 요청
import java.net.http.*;
import java.net.URI;
import java.util.concurrent.*;

public class NonBlockingHttpExample {
    public static void main(String[] args) {
        System.out.println("Start");

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://example.com"))
            .build();

        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenAccept(response -> {
                System.out.println("Response: " + response.body());
            })
            .exceptionally(e -> {
                e.printStackTrace();
                return null;
            });

        System.out.println("Doing other work...");

        // 메인 스레드가 종료되지 않도록 잠시 대기
        try {
            Thread.sleep(5000); // 예시를 위해 잠시 대기
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("End");
    }
}
  • 네트워크 요청을 보내고 바로 다음 작업을 수행함
  • 응답이 오면 콜백을 통해 결과를 처리함
  • Thread.sleep은 프로그램이 바로 종료되지 않도록 잠시 대기하는 용도이며, 실제 논블로킹 프로그램에서는 메인 스레드가 다른 작업을 수행함

블로킹과 논블로킹의 차이점

  • 제어권의 이동 여부 : 블로킹은 제어권이 호출된 함수로 넘어가고, 논블로킹은 호출자에게 남아 있음
  • 다른 작업 수행 가능 여부 : 블로킹은 다른 작업을 수행하지 못하지만, 논블로킹은 다른 작업을 동시에 수행할 수 있음

동기/비동기와 블로킹/논블로킹의 관계

개념의 혼동과 차이점

  • 동기/비동기는 작업의 완료 여부와 순서에 관한 개념
  • 블로킹/논블로킹은 제어권의 이동과 다른 작업 수행 가능 여부에 관한 개념
  • 이 둘은 서로 다른 차원에서 작업의 수행 방식을 설명하지만, 실제 프로그래밍에서는 혼용되어 사용되기도 함

제어권의 관점에서 본 블로킹/논블로킹

  • 제어권은 함수나 프로세스의 실행 흐름을 제어할 수 있는 권한
  • 블로킹은 호출된 함수가 작업을 완료할 때까지 제어권을 가지고 있음
  • 논블로킹은 호출자는 제어권을 유지하며, 호출된 함수의 작업 완료를 기다리지 않음

동기/비동기와 블로킹/논블로킹의 조합

Sync Blocking

  • 동기 + 블로킹
  • 다른 작업이 완료될 때까지 대기하고, 작업의 완료 여부를 확인하여 순차적으로 처리함

JAVA 예제

// 동기 + 블로킹 방식의 파일 처리
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;

public class SyncBlockingExample {
    public static void main(String[] args) throws IOException {
        System.out.println("Start");

        byte[] data1 = Files.readAllBytes(Paths.get("file1.txt"));
        System.out.println("File1 content: " + new String(data1));

        byte[] data2 = Files.readAllBytes(Paths.get("file2.txt"));
        System.out.println("File2 content: " + new String(data2));

        byte[] data3 = Files.readAllBytes(Paths.get("file3.txt"));
        System.out.println("File3 content: " + new String(data3));

        System.out.println("End");
    }
}
  • 파일을 하나씩 읽고, 읽기가 완료될 때까지 다음 작업을 수행하지 않음
  • 작업이 순차적으로 이루어짐

Async Non-Blocking

  • 비동기 + 논블로킹
  • 다른 작업을 수행하는 동안에도 제어권을 유지하고, 작업의 완료 여부에 관심 없이 다음 작업을 수행함

JAVA 예제

// 비동기 + 논블로킹 방식의 파일 처리
import java.nio.file.*;
import java.util.concurrent.*;

public class AsyncNonBlockingExample {
    public static void main(String[] args) throws Exception {
        System.out.println("Start");

        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            try {
                byte[] data = Files.readAllBytes(Paths.get("file1.txt"));
                System.out.println("File1 content: " + new String(data));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            try {
                byte[] data = Files.readAllBytes(Paths.get("file2.txt"));
                System.out.println("File2 content: " + new String(data));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
            try {
                byte[] data = Files.readAllBytes(Paths.get("file3.txt"));
                System.out.println("File3 content: " + new String(data));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        System.out.println("All file read requests initiated.");
        System.out.println("End");

        // 모든 작업이 완료될 때까지 대기
        CompletableFuture.allOf(future1, future2, future3).get();
    }
}
  • 파일 읽기 요청을 동시에 보내고, 완료 여부와 상관없이 다음 코드를 실행함
  • 작업이 완료되면 각자의 콜백에서 결과를 처리함

Sync Non-Blocking

  • 동기 + 논블로킹
  • 실제로는 비동기 + 블로킹에 가까움
  • 제어권을 유지하면서도, 작업의 완료 여부를 확인하여 순차적으로 처리함

JAVA 예제

// 동기 + 논블로킹 방식의 파일 처리
import java.nio.file.*;
import java.util.concurrent.*;

public class SyncNonBlockingExample {
    public static void main(String[] args) throws Exception {
        System.out.println("Start");

        ExecutorService executor = Executors.newSingleThreadExecutor();

        Future<byte[]> future = executor.submit(() -> {
            return Files.readAllBytes(Paths.get("file.txt"));
        });

        // 다른 작업 수행 가능
        System.out.println("Doing other work...");

        // 작업의 완료 여부를 확인하여 순차적으로 처리
        byte[] data = future.get(); // 완료될 때까지 대기
        System.out.println("File content: " + new String(data));

        executor.shutdown();

        System.out.println("End");
    }
}
  • 작업을 백그라운드에서 실행하면서도, 결과를 필요로 할 때까지 다른 작업을 수행함
  • future.get()을 호출하여 결과를 기다리며, 이때는 블로킹됨

Async Blocking

  • 비동기 + 블로킹
  • 다른 작업의 완료 여부에 관심 없이 다음 작업을 수행하지만, 제어권을 잃어 다른 작업을 수행하지 못함

특징

  • 일반적으로 잘 사용되지 않음
  • 작업은 비동기로 시작하지만, 호출자는 제어권을 잃어버림
profile
< 너만의 듀얼을 해!!! )

0개의 댓글