[OS] Thread 정리
이번 주는,,, 해외여행 등의 이슈로 굉장히 여유가 없는 한 주기 때문에 친구와 운영체제 스터디를 하며 정리한 내용을 올려보고자 한다.
대부분의 이론 내용은 '공룡책' 을 참고했다.
4.1 Overview
Thread: CPU utilization의 기본 단위
thread ID, program counter(PC), register set, stack 으로 구성된다.
같은 프로세스 안의 다른 스레드들과
공유: code section, data section, 기타 os 자원(files, signals) 들
각자: registers, stack, PC
Multithreading의 이점
1. Responsiveness
- 잠시 막혔거나 시간 오래 걸리는 작업 해야할때 프로그램이 멈추지 않고 진행
2. Resource Sharing
프로세스 간 자원 공유는 프로그래머가 직접 설정한 shared memory와 message passing 을 통해서 가능하지만, 같은 프로세스 안의 threads는 memory와 resource를 공유함
동일한 address space 안에서 서로 다른 일을 하는 threads를 가지는 이점
3. Economy
- threads는 자원을 공유하기때문에 더욱 경제적. context switching도 빠름
4. Scalability
- multiprocessor architecture에서 병렬 처리에 큰 이점 가짐
- 잠시 막혔거나 시간 오래 걸리는 작업 해야할때 프로그램이 멈추지 않고 진행
2. Resource Sharing
프로세스 간 자원 공유는 프로그래머가 직접 설정한 shared memory와 message passing 을 통해서 가능하지만, 같은 프로세스 안의 threads는 memory와 resource를 공유함
동일한 address space 안에서 서로 다른 일을 하는 threads를 가지는 이점
3. Economy
- threads는 자원을 공유하기때문에 더욱 경제적. context switching도 빠름
4. Scalability
- multiprocessor architecture에서 병렬 처리에 큰 이점 가짐
4.2 Multicore Programming
Multicore:
한 개의 프로세싱 칩에 여러개의 컴퓨팅 코어를 넣어서 OS에게 각 코어가 서로 다른 CPU 처럼 보이게 하는 시스템
multithread 시스템은 multicore 환경에 큰 이점을 제공함
싱글코어 시스템에서 concurrency 찾아봤자 요렇게 사이사이에 끼워 넣는게 최선임
parallel은 될 수 없고, 그냥 process switching을 통해 병렬처리가 되는 ‘척’ 한거임

멀티코어 시스템에선 코어마다 서로 다른 thread를 할당해줌으로서 완전한 병렬처리 (parrallel)를 제공 가능
멀티코어 시스템 프로그래밍의 도전 과제:
1. Identifying Tasks
- 애플리케이션을 분석해서 분리/병렬처리 가능한 부분들을 찾는 것
2. Balance
- 개별 테스크들이 비슷한 양의 비슷한 일들을 하게 해야 함
3. Data Splitting
- 서로 다른 코어에서 돌아갈 데이터들을 적절히 분리해줘야 함
4. Data Dependency
- 서로 다른 태스크 간에 의존성을 확인해서 작업 실행을 동기화해야 함
5. Testing and Debugging
- 여러 코어에서 동시에 실행되는 프로그램을 테스트하고 디버깅
병렬 처리의 유형
Data Parallelism
- 동일한 데이터의 하위 집합을 여러 컴퓨팅 코어에 분배하고 각 코어에서 동일한 작업을 수행하게 함
Task Parallelism
태스크(스레드)를 여러 컴퓨팅 코어에 분배하고, 각 스레드는 고유한 작업을 수행함
서로 다른 스레드는 동일한 데이터에서 작업할 수도 있고, 다른 데이터에서 작업할 수도 있음
4.3 Multithreading Models
스레드는 유저 레벨에서 유저 스레드로, 커널에선 커널 스레드로 지원
유저 스레드는 커널의 서포트 없이 관리되고, 커널 스레드는 OS에서 직접 관리
유저 스레드 - 커널 스레드 간 관계는 일반적으로 3 개로 나뉨:
- 다대일, 일대일, 다대다
[1] Many-to-One Model
여러 유저 레벨 스레드가 하나의 커널 스레드에 매핑됨
스레드 관리는 유저 공간 안의 thread library에서 수행 → 효율적
단점)
한 스레드가 blocking system call을 호출하면 전체 프로세스가 막힘
다중 코어 시스템에서 병렬 실행 불가능
예) ‘Green Threads’,,, 이 방식은 멀티코어의 이점을 살리지 못해서 이제 안쓰인당
[2] One-to-One Model

각 유저 스레드가 하나의 커널 스레드에 매핑됨
blocking system call 호출해도 다른 스레드가 실행될 수 있어서 동시성 제공 가능
단점
- 유저 스레드를 만드는 만큼 커널 스레드도 추가로 생성해야돼서 시스템 성능에 부담을 줄 수 있음
예) Linux, Windows 운영 체제
[3] Many-to-Many Model

여러 유저 스레드가 더 적거나 같은 수의 커널 스레드에 대중 매핑됨
커널의 스레드 수는 특정 애플리케이션이나 기기에 따라 다를 수 있음
유저 스레드를 원하는 만큼 생성할 수 있고, 커널 스레드는 멀티 프로세서에서 병렬로 실행 가능
단점
- 구현이 어려움 → 대부분 시스템에서 일대일 모델 사용
variation : two-level model
유저 스레드가 커널 스레드에 바인딩 되는 경우

4.4 Thread Libraries
개요
- 스레드 라이브러리는 프로그래머에게 스레드를 생성/관리할 수 있는 API를 제공함
구현 방식
User Space Library: 커널 지원 없이 유저 공간에서만 작동. 함수 호출은 로컬 함수 호출로 처리
Kernal-Level Library: 운영체제에서 직접 지원. 함수 호출은 커널 호출로 처리됨
주요 스레드 라이브러리
POSIX Pthreads : 유저 레벨 + 커널 레벨
- 글로벌로 선언된 데이터는 동일한 프로세스의 모든 스레드가 공유
Windows Thread Library : 커널 레벨
- 글로벌로 선언된 데이터는 동일한 프로세스의 모든 스레드가 공유
Java Thread API : Java 프로그램 내에서 직접 스레드 생성/관리함
JVM이 호스트 운영 체제 위에서 실행 → 호스트 시스템의 스레드 라이브러리 사용해서 구현됨
글로벌 데이터 개념이 없어서 스레드 간 데이터 공유를 명시적으로 처리
스레드 생성 전략
비동기(asynchronous) 스레딩
부모가 자식 스레드 생성한 후 부모 스레드가 계속 실행됨
독립적이라 데이터 공유 적음
주로 반응형 사용자 인터페이스 설계에 사용
동기(synchronous) 스레딩
부모가 자식 스레드 생성하고, 모든 자식 스레드가 종료될 때까지 기다림
자식 스레드가 작업을 완료한 후 부모 스레드와 합쳐짐
데이터 공유 많음
Pthreads (POSIX Threads)
POSIX 표준의 확장으로, 유닉스 계열 시스템에서 주로 사용
- 유닉스, 리눅스, macOS 등
유저 레벨 라이브러리와 커널 레벨 라이브러리 모두로 구현 가능
특징
다중 스레드를 쉽게 만들고 관리할 수 있는 함수들 제공
글로벌 데이터 공유 : 전역적으로 선언된 데이터는 동일한 프로세스 내의 모든 스레드에서 접근 가능함 (Windows와 마찬가지)
높은 이식성 : POSIX 표준 따라서 여러 플랫폼에서 일관된 동작 보장함
Windows Threads
Windows 운영 체제에서 제공하는 커널 레벨의 스레드 라이브러리
스레드 생성, 동기화, 종료 등의 기능을 Windows API를 통해 제공
특징
Windows Native 환경에서 사용 -
CreateThread,ExitThread,WaitforSingleObject등의 함수를 통해 스레드 관리글로벌 데이터 공유 : 전역적으로 선언된 데이터는 동일한 프로세스 내의 모든 스레드에서 접근 가능함 (Pthreads와 마찬가지)
강력한 동기화 도구 : critical section, mutex, semaphore 등 동기화 메커니즘 제공
Java Threads
Java 언어에서 제공하는 스레드 라이브러리로, Java 프로그램 내에서 스레드를 생성하고 관리할 수 있음
JVM(Java Virtual Machine)이 호스트 운영 체제의 스레드 라이브러리를 사용하여 구현함
- Windows에서는 Windows 스레드 API를, 유닉스 계열 시스템에서는 Pthreads를 사용할 수 있음
사용
Thread 클래스: Java에서 스레드를 생성하려면 Thread 클래스를 확장하거나 Runnable 인터페이스를 구현
start(),run(),join()등의 메서드를 통해 스레드를 제어
특징
명시적 데이터 공유: Java에는 전역 데이터의 개념이 없어서 스레드 간 데이터를 공유하려면 명시적으로 공유 객체를 전달해야 함
플랫폼 독립성: JVM 위에서 실행되므로, 플랫폼에 관계없이 일관된 스레드 동작을 보장함
동기화 도구:
synchronized키워드와java.util.concurrent패키지를 통해 동기화 메커니즘을 제공함
4.+ 웹과 스레드
JavaScript와 멀티스레딩
- JS는 전통적으로 싱글스레드 환경에서 실행되지만, 멀티스레딩 지원을 위한 다양한 기술과 API를 제공함 - Web Workers, SharedArrayBuffer 등
JS의 싱글 스레드 환경
기본적으로 JS는 단일 스레드에서 실행됨. Event Loop 메커니즘을 통해 비동기 작업을 처리할 수 있도록 설계됨
Event Loop: 콜백 함수, 프로미스(promise), async/await 등의 비동기 작업 관리. 메인 스레드가 블로킹되지 않도록 함
JS의 메인 스레드인 Event Loop가 싱글 스레드이기 때문에 JS는 싱글 스레드
싱글스레드의 특징
context switch 작업을 요구하지 않음 → 한 프로세스가 CPU 사용중인 상태에서 다른 프로세스가 CPU 사용하도록 하기 위해 프로세스의 상태(context)를 보관하고 새로운 프로세스의 상태를 적재하지 않음
자원 접근에 대한 동기화 신경쓰지 않아도 됨 (동일한 자원에 접근하는 여러 스레드가 없기 때문)
CPU만을 사용한 연산 수행 시, 멀티스레드 환경보다 속도가 빠름 (context switching 과정의 시간 절약하기 때문)
프로그래밍 난이도 낮고, 자원 적게 소모
연산량 많은 작업 수행시 해당 작업 마무리 될때까지 아무런 동작 못함 (blocking)
JS의 멀티스레딩 ‘척’
싱글스레드지만, Web API와 Event Loop, Callback Queue를 통해 멀티스레드처럼 call back 함수나 비동기 처리를 할 수 있음
Call Stack
JS에서 수행해야 할 함수들을 순차적으로 담아 처리
함수 호출이 발생하면 Call Stack에 함수가 추가되고, 실행이 완료되면 스택에서 제거
main.js에서 new Worker(’worker.js’) 호출해서 Worker를 생성함. 생성은 WebAPI에 의해 처리되고, Worker 스레드 시작됨.
Web API
웹 브라우저에서 제공하는 API로, 비동기 작업 처리
예를 들어 setTimeout, XMLHttpRequest, fetch, DOM 이벤트 핸들러, Web Workers 등
오래 걸릴 것 같은 요청을 Call Stack에서 이곳으로 이동하고 나중에 처리함
Worker 생성 요청을 처리하여 새로운 스레드를 생성함. 메인 스레드는 worker.postMessage('Start calculation')으로 Worker에 메시지를 보냄.
Task Queue (Callback Queue)
비동기 작업이 완료되면 그 콜백 함수는 Task Queue에 추가됨
이벤트 루프는 Call Stack이 비어 있을 때 Task Queue에서 콜백 함수를 가져와 실행함
Event Loop
이벤트 루프는 Call Stack과 Task Queue를 모니터링하여 Call Stack이 비어 있을 때 Task Queue의 작업을 Call Stack에 추가함
이를 통해 비동기 작업이 비동기적으로 처리되지만, 순차적으로 실행되는 것처럼 보이게 함
Task Queue와 Event Loop에선 Worker 스레드가 메시지를 수신하고 작업을 수행함. 작업이 완료되면 postMessage(sum)으로 결과를 메인 스레드에 전달함. 메인 스레드의 이벤트 루프는 Task Queue에서 메시지를 가져와 worker.onmessage 핸들러를 실행함.
위와 같은 것들을 통해 싱글스레드 환경에서 비동기 작업 관리함.
Web Worker는 별도의 스레드를 생성하여 백그라운드에서 JavaScript 코드를 실행함.
메인 스레드와 독립적으로 실행됨 → 멀티스레딩 환경이 구현됨
Web Workers
웹 브라우저 환경에서 백그라운드 스크립트를 실행할 수 있도록 지원하는 API
웹 애플리케이션이 메인 스레드(UI 스레드)의 성능과 응답성을 유지하면서 복잡하고 시간이 많이 걸리는 작업을 수행할 수 있음
특징
백그라운드 실행:
메인 스레드와 분리된 별도의 스레드를 백그라운드에서 생성/실행함
이를 통해 무거운 계산 작업이나 데이터 처리를 메인 스레드의 성능 저하 없이 수행
예제 코드
main.js
// Worker 생성 const worker = new Worker('worker.js'); // 메인 스레드에서 메시지 전송 worker.postMessage('Start calculation'); // Worker로부터 메시지 수신 worker.onmessage = function(event) { console.log('Result from worker:', event.data); }; // Worker 종료 worker.terminate();worker.js
// Worker에서 메시지 수신 onmessage = function(event) { console.log('Received from main thread:', event.data); // 긴 계산 작업 (예: 1부터 1억까지 합) let sum = 0; for (let i = 1; i <= 1e8; i++) { sum += i; } postMessage(sum); };런타임 과정
메인 스레드에서 Worker 생성:
main.js에서 new Worker('worker.js')를 호출하면, 브라우저의 Web API가 새로운 Worker 스레드를 생성함
Worker는 별도의 스레드에서 실행되며, 독립적인 Call Stack을 가짐
메시지 전송:
메인 스레드에서 worker.postMessage('Start calculation')을 호출하면, 이 메시지는 Web API를 통해 Worker 스레드로 전달됨
Worker 스레드의 onmessage 핸들러가 호출되어 메시지를 처리함
Worker 스레드에서 작업 수행:
Worker 스레드는 독립적인 Call Stack을 사용하여 긴 계산 작업을 수행함
계산이 완료되면 postMessage(sum)을 호출하여 결과를 메인 스레드로 보냄
메인 스레드에서 결과 수신:
Worker의 postMessage(sum) 호출은 Web API를 통해 메인 스레드의 Task Queue에 콜백을 추가함
이벤트 루프는 메인 스레드의 Call Stack이 비어 있을 때 Task Queue에서 콜백을 가져와 실행함
worker.onmessage 핸들러가 호출되어 결과를 처리함
크롬의 Tab은 프로세스일까 스레드일까?
참고) 최신 웹브라우저 들여다보기 (1부) | Blog | Chrome for Developers
크롬의 탭은 프로세스이다 (멀티 프로세스)
멀티 프로세스를 사용하며 IPC(Inter Process Communication, 프로세스 간 통신)을 사용함
하나의 탭에서 크래시나 메모리 누수가 발생하더라도 다른 탭에 영향을 미치지 않음
특정 탭이 응답하지 않을 때는 동작하지 않는 탭의 프로세스를 재시작 시켜버리는 방식을 사용함
참고로 탭은 한 프로세스만 지니는게 아니라 여러개의 프로세스를 지닌다
각 탭은 별도의 렌더링 프로세스를 가지고 있으며, 이 프로세스는 HTML, CSS, JavaScript를 해석하고 실행함
멀티 프로세스는 높은 성능을 요구하지만, 안정적이다
멀티 프로세스는 무겁다. 다만 크롬 브라우저에서 이런 구조를 선택할 수 밖에 없었던 이유는 웹 사이트를 개발자가 별도의 심사 없이 배포하기 때문임
악성 개발자가 모든 공유자원의 주도권을 가진 상태로 죽어버린다면 문제가 발생할 수 있음
이게 크롬이 메모리를 많이 사용하는 이유임 - 메모리 성능을 어느정도 포기하되 안정적이고 빠르고 안전한 사용자 경험을 선택
플러그인 및 확장 프로그램
플러그인과 확장 프로그램도 별도의 프로세스로 실행됨
플러그인 충돌이 발생해도 브라우저 전체가 다운되지 않고 해당 플러그인만 재시작하면 됨
REFERENCE
https://velog.io/@adultlee/JS는-어떻게-멀티스레드인척-할까
https://medium.com/@gyeong3un/javascript가-멀티스레드처럼-보일-수-있는이유-48a7159aaa66
https://medium.com/@sarthakvit/multithreading-using-javascript-cf9ad7cf9cfe

