주제 : JavaScript는 왜 싱글 스레드인가?

김혜진·2024년 7월 4일
0
JavaScript는 왜 싱글 스레드인지 알기 위해서 우선 스레드가 무엇인지 먼저 알아보겠습니다. 😀

스레드란?

스레드(thread)는 프로그램의 실행 흐름의 가장 작은 단위입니다.

여러 작업을 병렬로 처리할 수 있어 프로그램의 성능과 응답성을 높일 수 있습니다.

  1. 프로세스와 스레드의 차이점 :
  • 프로세스 : 독립적인 실행 단위로서 자체 메모리 공간을 가지고 있습니다.
  • 스레드 : 하나의 프로세스 내에서 실행되는 여러 흐름으로, 같은 메모리 공간을 공유합니다.

다음으로 스레드의 장점과 단점에 대해 알아보겠습니다.

  1. 스레드의 장점 :
  • 같은 프로세스 내 스레드는 메모리 공간을 공유하기 때문에 통신 오버헤드가 적습니다.
  • 독립적인 작업을 별도로 처리하여 코드 구조를 단순화할 수 있습니다.
  • 비동기 작업을 스레드로 분리하여 처리하면, 주 스레드가 블로킹 없이 다른 작업을 계속 수행할 수 있습니다.
  1. 단점:
  • 여러 스레드가 같은 자원에 접근할 때 동기화 문제가 발생할 수 있으며, 이를 관리하지 않으면 교착 상태 등의문제가 발생할 수 있습니다.

  • 스레드 간의 상호작용이 복잡해지면 디버깅이 어려워질 수 있습니다.

스레드에서도 다중 스레드(multithreading)와 싱글 스레드(single-threading)가 있는데, 이 둘의 특징과 차이점에 대해 알아보겠습니다.

싱글 스레드와 다중 스레드의 차이

  • 싱글 스레드

    1. 싱글 스레드 특징 :
    • 프로그램은 한 번에 하나의 작업만 처리합니다.

    • 모든 작업은 순차적으로 처리된다. 하나의 작업이 완료되어야 다음 작업이 시작됩니다..

    • 코드 구조가 상대적으로 간단하고 동기화 문제를 고려할 필요가 없습니다.

    • 개발과 디버깅이 쉬운 편입니다.

      장점 :

    • 구현이 간단하고, 동기화 문제를 신경 쓸 필요가 없습니다.

    • 유지 보수가 상대적으로 쉽습니다.

      단점:

    • CPU 자원을 충분히 활용하지 못할 수 있습니다.

    • 작업이 오래 걸리면 전체 프로그램의 응답성이 저할될 수 있습니다.

  • 다중 스레드

    1. 다중 스레드 특징 :
    • 여러 작업을 동시에 처리할 수 있어 CPU의 여러 코어를 효율적으로 활용할 수 있습니다.

    • 여러 스레드가 병렬로 작업을 수행하여 작업 속도를 높일 수 있습니다.

    • 동기화 문제를 해결하기 위해 추가적인 코드가 필요합니다.

      장점 :

    • 프로그램의 성능과 응답성이 향상됩니다.

    • CPU 자원을 보다 효율적으로 사용할 수 있습니다.

      단점:

    • 동기화 문제(ex. 경합 상태, 교착 상태 등)를 해결해야 합니다.

    • 디버깅과 유지 보수가 복잡합니다.

    • 스레드 생성과 관리에 따른 오버헤드가 발생할 수 있습니다.

  • 예시

    #싱글 스레드

    import time
    
    def task1():
          print("Task 1 시작")
          time.sleep(2)
          print("Task 1 완료")
    
    def task2():
          print("Task 2 시작")
          time.sleep(2)
          print("Task 2 완료")
    
    task1()
    task2()
    • ‘tastk1’이 완료된 후에야 ‘task2’가 시작됩니다.

      #다중 스레드

      import threading
      import time
      
      def task1():
             print("Task 1 시작")
             time.sleep(2)
             print("Task 1 완료")
      
      def task2():
            print("Task 2 시작")
            time.sleep(2)
            print("Task 2 완료")
            
      // 두 개의 스레드를 생성하여 동시에 실행
      thread1 = threading.Thread(target=task1)
      thread2 = threading.Thread(target=task2)
      
      thread1.start()
      thread2.start()
      
      thread1.join()
      thread2.join()
    • ‘task1’과 ‘tastk2’가 동시에 실행됩니다.

자바 스크립트란?

  • 자바스크립트(JavaScript)는 웹 페이지를 동적으로 만드는 프로그래밍 언어입니다.

    HTML과 CSS와 함께 웹의 중요한 구성 요소 중 하나이며 웹 페이지에 인터랙션을 추가합니다.
    예를 들어, 버튼을 클릭하면 경고 창이 뜨게 하거나, 폼을 제출하면 데이터를 서버로 보내는 등의 작업을 할 수 있습니다.

🗣 이제 JavaScript가 싱글 스레드인지 알아보겠습니다.

— 당시 웹 페이지는 단순하게 정보를 표시하는 정적인 문서였기 때문에 한 번에 하나의 작업만 처리해도 충분했습니다.

  • 이유

    JavaScript는 Brendan Eich에 의해 1995년에 10일 만에 개발되었고, 초기 목적은 웹 페이지의 사용자 인터페이스를 동적으로 제어하는 것이었기 때문에 간단한 동작과 검증을 추가하기 위해 설계되었습니다.

    따라서 하나의 작업만 처리해도 충분했기 때문에 속도와 단순함을 제공하는 것이 중요했습니다.

— JavaScript는 사용하기 쉬운 언어를 목표로 했으며 싱글 스레드는 이에 적합합니다.

  • 이유

    JavaScript는 주로 웹 브라우저 환경에서 동작하며, 이 환경은 싱글 스레드 모델에 최적화되어 있습니다.

    또한 싱글 스레드의 특성인 단순성과 예측 가능성을 유지하면서도, 복잡성을 낮추고 개발자가 코드를 더 쉽게 관리할 수 있도록 하기 위했습니다.

    싱글 스레드 모델을 통해, 개발자는 스레드 동기화 문제를 처리할 필요가 없으므로, 코드 작성과 유지 보수가 간단합니다.

    비동기 작업을 이벤트 루프와 콜백을 통해 처리함으로써, 복잡한 멀티 스레딩 모델 없이도 동시성을 지원할 수 있습니다.

    이는 최종적으로 JS의 목표에 부합하다고 할 수 있습니다.

— JavaScript는 싱글 스레드 모델과 잘 맞아 떨어집니다.

  • 이유

    첫 번째로 JavaScript는 이벤트 기반 언어로 설계되었습니다.

    여기서 이벤트 프로그래밍은 특정 사건에 응답하여 작업을 수행하는 프로그래밍 방식입니다.

    이벤트 기반 모델은 상대적으로 단순하며, 다양한 비동기 작업을 확장성 있게 관리할 수 있으며 웹 환경에서는 사용자 입력, 네트워크 요청, 타이머 등의 작업이 빈번하게 발생합니다. 이때 작업은 비동기적으로 처리되어야 하며, 이벤트 기반 모델은 이것을 효과적으로 관리할 수 있습니다.

    두 번째, JavaScript의 이벤트 루프는 비동기 작업을 처리하는 중요한 메커니즘입니다.

    이벤트 루프는 호출 스택(현재 실행 중인 함수가 저장되는 구조)과 태스크 큐(비동기 작업이 완료되었을 때 실행될 콜백 함수가 저장되는 구조)를 관리하여 비동기 작업을 처리합니다.

    호출 스택이 비어 있을 때, 이벤트 루프는 태스크 큐에서 작업을 가져와 실행합니다. 이는 비동작업을 순차적으로 처리하며, 동시성 문제를 피하면서도 여러 작업을 처리할 수 있게 합니다.

    싱글 스레드 모델은 코드가 한 번에 하나의 작업만 수행하도록 보장하므로, 동시성 문제를 피할 수 있습니다.
    그렇기 때문에 JavaScript인 이벤트 기반 프로그래밍의 단순성과 잘 맞아 떨어집니다.

    이러한 이유들로 인해, JavaScript의 이벤트 기반 프로그래밍 모델은 싱글 스레드 환경에서 매우 효과적으로 동작할 수 있습니다.

— JavaScript는 싱글 스레드입니다.

  • 이유

    Node.js 관점에서 바라 보자면, Node.js의 싱글 스레드 모델은 비동기적인 처리를 통해 높은 성능과 확장성을 제공하면서도 코드의 단순성과 안정성을 유지할 수 있습니다.

    비동기 처리란 특정 로직의 실행이 끝날때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것을 비동기 처리라고 합니다.

    여기서 Node.js란?

    JavaScript를 서버 사이드(서버 측에서 즉시 처리하는 것)에서 실행할 수 있게 만든 런타임 환경(애플리케이션이 운영체제의 시스템 자원에 엑세스 할 수 있도록 해주는 환경)입니다.

    Node.js의 싱글 스레드 모델

    Node는 여러 개의 스레드를 가지고 있지만, 자바스크립트를 실행하는 스레드는 단 하나이기 때문에 Node를 싱글 스레드라고 합니다.

    그리고 그 싱글 스레드를 이벤트 루프라고 합니다.

    Node는 이벤트 기반의 플랫폼이므로 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식으로 작동 합니다.

    이는 곧 Node에서 일어나는 모든 처리는 일련의 Call Back을 처리하는 것과 같다고 할 수 있습니다.

    JavaScript Engine?

    JavaScript Engine은 Memory Heap과 Call stack으로 이뤄져 있습니다.

    Memory Heap은 메모리 할당이 일어나는 곳이고, Call Stack은 코드 실행에 따라 호출 스택이 쌓이는 곳입니다.

    JavaScript Engine은 Call stack에 쌓인 Execution Context에 따라 위에서부터 차례로 실행이 일어나는 곳이기 때문에 비동기 처리를 할 수 없습니다.

    따라서 비동기 처리가 필요하게 될 경우 Node를 통해 Libuv 라이브러리에서 제공하는 비동기 처리를 하게 됩니다.

    이때 Libuv가 제공하는 것이 이벤트 루프 입니다.

    이 이벤트 루프가 JavaScript Engine으로부터 넘겨받은 비동기 작업을 OS의 비동기 Interface에 넘겨주거나 스레드 풀에 할당합니다.

    이후 할당한 작업이 끝나면 Task Queue에 Call Back 함수를 전달한 후 Call Stack이 비게 될 경우 차례로 Call Stack으로 다시 넘겨 실행하게 합니다.

    이 이벤트 루프가 싱글 스레드이고 실제적으로 JavaScript를 처리할 수 있는 메인 스레드이기 때문에 Node.js를 싱글 스레드 라고 합니다.

    결론

    1. JavaScript는 Memory Heap과 Call stack으로 이뤄져 있으며, Call stack에 쌓인 Execution Context에 따라 위에서부터 차례로 실행이 일어나는 곳이기 때문에 비동기 처리를 할 수 없습니다. 비동기 처리가 필요하게 될 경우 Node를 통해 Libuv 라이브러리에서 제공하는 비동기 처리를 하게 됩니다.

    2. Node는 자바스크립트를 서버에서 즉시 처리할 수 있도록 해주며 운영체제의 시스템 자원에 엑세스 할 수 있도록 해주는 환경이며, 여러개의 스레드를 가지고 있지만, 자바스크립트를 실행하는 스레드는 단 하나이기 때문에 싱글 스레드라고 불리며, 이 싱글 스레드를 이벤트 루프라고 합니다.

    3. Libuv 라이브러리는 자바스크립트를 서버에서 즉시 처리할 수 있도록 해주며 운영체제의 시스템 자원에 엑세스 할 수 있도록 해주는 환경인 Node에서 특정 로직의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행할 수 있도록 해주는 C언어 기반 라이브러리입니다. 이 때 Libuv가 제공하는 것이 이벤트 루프 입니다. 이 이벤트 루프가 싱글 스레드이고 실제적으로 JavaScript를 처리할 수 있는 메인 스레드이기 때문에 Node.js를 싱글 스레드라고 합니다.

      따라서, JavaScript는 싱글 스레드인 Node를 통해 서버에서 즉시 작업을 처리하며, 운영체제의 시스템 자원에 엑세스를 하므로, 싱글 스레드에서 Node를 통해 높은 효율로 작동하므로 싱글 스레드입니다.**

— 자원 관리와 확장성 측면에서 적합합니다.

  • 이유

    JavaScript는 메모리 사용 및 실행 컨텍스트 전환 비용을 줄일 수 있습니다.

    여기서 실행 컨텍스트란 JavaScript 코드가 실행될 때 생성되는 추상적인 개념 입니다.

    멀티 스레드 환경에서는 각 스레드가 별도의 스택 메모리를 사용하며, 이는 메모리 사용량이 증가할 수 있습니다. 반면 자바스크립트의 싱글 스레드 모델을 하나의 스택 메모리만 사용하므로, 메모리 사용량이 상대적으로 적습니다.

profile
안녕하세요

0개의 댓글