Blocking vs Non-Blocking

이동근·2023년 3월 26일
0

어렴풋이 감으로만 알고있던 Synchronous/Asynchronous와 Blocking/Non-Blocking에 대한 개념을 정리해보려고 합니다.

해당 글은 ChatGPT의 답변을 구글링하여 체크 한 후 정리한 글입니다.

개념


동기(Synchronous) / 비동기(Asynchronous)

  • 이 개념은 호출자(메서드를 호출한 코드)가 작업이 완료될 때까지 기다리는 방식과 관련이 있음.
  • Synchronous : 호출자가 작업이 완료될 때까지 기다리는 작업 방식
  • Asynchronous : 호출자가 작업을 시작하고, 완료되기 전에 다른 작업을 계속할 수 있는 작업 방식

Blocking / Non-Blocking

  • 이 개념은 쓰레드가 작업을 처리하는 방식과 관련이 있다.
  • Blocking : 작업이 완료될 때까지 실행 스레드를 대기 상태로 만드는 작업 방식
  • Non-Blocking : 작업이 완료되지 않은 상태에서도 실행 스레드를 대기 상태로 만들지 않는 작업 방식

Synchronous/Asynchronous와 Blocking/Non-Blocking 개념은 서로 관련이 있지만, 완전히 동일하지는 않습니다.

일반적으로 동기 작업은 Blocking 방식으로 구현되고, 비동기 작업은 Non-Blocking 방식으로 구현되지만, 이러한 관계는 항상 성립하지는 않습니다.

어떤 경우에는 동기 작업이 Non-Blocking 방식으로 처리되거나, 비동기 작업이 Blocking 방식으로 처리될 수도 있습니다.

하지만 이러한 경우는 드물고, 일반적으로는 동기 작업이 Blocking으로 작동되고, 비동기 작업이 Non-Blocking으로 작동됩니다.

아래에서는 편의상 동기방식과 Blocking방식을 묶어서 지칭하고, 비동기 방식과 Non-Blocking방식을 묶어서 지칭하겠습니다.

예시


Blocking과 Non-Blocking 코드의 차이를 설명하기 위해 간단한 예제로 C# 코드를 작성하겠습니다.

먼저, Blocking 코드를 살펴보겠습니다. 이 경우에는 작업이 완료될 때까지 프로그램이 대기합니다. 파일을 읽는 동안 다른 작업이 중단됩니다.

using System;
using System.IO;

class BlockingExample
{
    static void Main()
    {
        Console.WriteLine("Starting file read operation...");
        string fileContent = File.ReadAllText("example.txt");
        Console.WriteLine("File content: ");
        Console.WriteLine(fileContent);
        Console.WriteLine("File read operation completed.");
    }
}

이제 Non-Blocking 코드를 살펴봅시다. 이 경우에는 작업이 완료되지 않아도 프로그램이 계속 진행됩니다. 작업이 완료되면 이후에 결과를 처리하는 방식입니다. 이 예제에서는 async와 await 키워드를 사용하여 비동기 파일 읽기를 수행합니다.

using System;
using System.IO;
using System.Threading.Tasks;

class NonBlockingExample
{
    static async Task Main()
    {
        Console.WriteLine("Starting file read operation...");
        Task<string> fileReadTask = File.ReadAllTextAsync("example.txt");
        Console.WriteLine("File read operation started. Waiting for the result...");

        string fileContent = await fileReadTask;
        Console.WriteLine("File content: ");
        Console.WriteLine(fileContent);
        Console.WriteLine("File read operation completed.");
    }
}

'Blocking' 예제에서는 파일을 읽는 작업이 완료될 때까지 프로그램이 대기합니다. 반면에 'Non-Blocking' 예제에서는 파일 읽기 작업이 시작된 후 프로그램이 계속 진행되며, 결과를 기다립니다. 결과가 준비되면 처리를 계속합니다.

결론적으로, Blocking 코드는 작업이 완료될 때까지 프로그램이 대기하는 반면, Non-Blocking 코드는 작업이 완료되지 않아도 프로그램이 계속 진행되어 성능이 향상됩니다.

더 자세히 알아보겠습니다.

Blocking (동기) 코드

동기 코드에서는 작업이 순차적으로 실행됩니다. 이전 작업이 완료될 때까지 다음 작업은 시작되지 않습니다. 이러한 동기 코드의 예는 파일을 읽거나 쓰는 작업입니다.

Console.WriteLine("Reading file...");
string content = File.ReadAllText("example.txt");
Console.WriteLine("File content: " + content);
Console.WriteLine("Finished reading file.");

이 코드는 "Reading file..."을 출력한 다음, 파일을 읽는 작업이 완료될 때까지 기다립니다. 파일을 다 읽으면 "File content: "와 함께 파일의 내용을 출력하고 "Finished reading file."을 출력합니다.

Non-Blocking (비동기) 코드

비동기 코드는 작업이 완료되지 않아도 다음 작업이 시작될 수 있습니다. 비동기 코드는 여러 작업을 동시에 실행할 수 있으며, 이를 통해 프로그램의 전체 성능과 응답성이 향상됩니다.

C#에서는 async와 await 키워드를 사용하여 비동기 코드를 작성합니다. 위의 동기 코드를 비동기 코드로 변환해 보겠습니다.

Console.WriteLine("Reading file...");
string content = await File.ReadAllTextAsync("example.txt");
Console.WriteLine("File content: " + content);
Console.WriteLine("Finished reading file.");

이 코드는 "Reading file..."을 출력한 다음, await 키워드를 사용하여 파일 읽기 작업이 완료될 때까지 기다립니다. 그러나 이 기다림은 스레드를 차단하지 않습니다. 대신, 이 시점에서 현재 메서드의 실행이 일시 중단되고, 호출자에 제어가 반환됩니다. 이렇게 하면 다른 작업을 동시에 처리할 수 있습니다.

스레드를 차단하는 것은 일반적으로 스레드가 작업을 완료하는 것을 기다리는 동안 아무것도 하지 않고 대기하는 것을 의미합니다. 스레드 차단이 발생하면 해당 스레드는 그 시점에서 다른 작업을 수행할 수 없으며, 대기 중인 작업이 완료될 때까지 시스템 리소스를 사용하지 못합니다.

호출자에게 제어를 반환하는 것은 함수 또는 메서드가 실행 중에 다른 함수 또는 메서드로부터 호출되었을 때, 실행 중인 함수 또는 메서드가 일시 중단되고 호출한 메서드로 제어가 다시 이동하는 것을 의미합니다. 제어가 반환되면 호출자는 다른 작업을 계속 수행할 수 있습니다.

비동기 프로그래밍에서는 await 키워드를 사용하여 작업이 완료될 때까지 기다리지만 스레드를 차단하지 않습니다. 대신, 해당 메서드의 실행이 일시 중단되고, 호출자에게 제어가 반환됩니다.

다시 소스코드에 대한 설명으로 돌아가자면, 파일 읽기 작업이 완료된 후 메서드의 실행이 다시 시작되고, "File content: "와 함께 파일의 내용을 출력한 다음 "Finished reading file."을 출력하며 끝납니다.

저는 await이라는 키워드가 비동기 메서드를 동기로 처리하기위한 키워드인줄 알고 있었습니다. 하지만 await을 사용해도 여전히 비동기로 작업을 처리하는 것임을 알게 되었습니다.

string fileContent = await File.ReadAllTextAsync("example.txt");

이 코드는 await 키워드를 사용하며 여전히 비동기(non-blocking)입니다. await는 비동기 작업이 완료될 때까지 현재 메서드의 실행을 일시 중단하고, 작업이 완료되면 결과를 반환합니다. 그러나 이러한 일시 중단이 다른 스레드를 차단하지 않기에 Blocking과는 다릅니다. 해당 스레드는 작업이 완료되기 전까지 다른 작업에 투입 될 수 있으며. 시간이 지나 완료된 비동기 작업은 기존의 스레드 또는 다른 스레드에 의해 이어서 처리 될 수 있습니다.

Blocking 코드의 경우 스레드가 차단되어 해당 스레드는 다른 작업에 투입 될 수 없고, 해당작업의 완료만 기다리게 됩니다.

결론


비동기 프로그래밍의 주요 이점은 다음과 같습니다.

응답성 향상: 애플리케이션은 긴 작업을 수행하는 동안 다른 작업을 처리할 수 있으므로 사용자 인터페이스가 더 반응적으로 느껴집니다. 예를 들어, 웹 서버 애플리케이션에서는 한 요청이 처리되는 동안 다른 요청을 동시에 처리할 수 있습니다.

리소스 활용: 비동기 작업은 일시적으로 메서드의 실행을 중단하고 호출자에게 제어를 반환합니다. 이를 통해 시스템 리소스가 여러 작업 간에 공유되어 시스템 전체의 성능이 향상됩니다.

스케일 아웃: 비동기 작업은 동시에 여러 작업을 처리할 수 있으므로 애플리케이션을 수평적으로 확장하는 데 도움이 됩니다. 예를 들어, 웹 서버 애플리케이션은 동시에 많은 요청을 처리할 수 있습니다.

취소 가능성: 비동기 작업은 취소 토큰을 통해 실행 중인 작업을 취소할 수 있습니다. 이를 통해 불필요한 작업을 중단하고 리소스를 절약할 수 있습니다.

비동기 프로그래밍의 한 가지 단점은 코드의 복잡성이 증가할 수 있다는 것입니다. async와 await 키워드를 사용하면 비동기 코드를 동기 코드와 유사한 방식으로 작성할 수 있지만, 여전히 에러 처리, 취소, 동시성 제어 등의 추가 고려 사항이 있습니다.

그러나 이러한 복잡성에도 불구하고, 애플리케이션의 성능과 응답성을 높이기 위해 비동기 프로그래밍을 사용하는 것이 매우 중요합니다. 특히 사용자 인터페이스, 웹 서버, 데이터 처리 등과 같이 동시에 여러 작업을 처리해야 하는 애플리케이션에서 이러한 이점이 더욱 명확해집니다.

profile
PompitzGeneral

0개의 댓글