쓰레드

hwakyungChoi·2020년 10월 24일
0

쓰레드 (Thread)

Thread?

● 쓰레드 (Thread)
– 프로그램 내부의 흐름, 맥

class Test {
public static void main(String[] args) {
int n = 0;
int m = 6;
System.out.println(n+m);
while (n < m)
n++;
System.out.println("Bye");
}

Multithreads

● 다중 쓰레드 (Multithreads)
– 한 프로그램에 2개 이상의 맥 => 맥이 여러 개 있다?
- 맥은 일반적으로 하나있는데 싱글 쓰레드
- 맥이 2개 이상이면 다중 쓰레드 => 동시에 돌 수 있는 이유?
– 맥이 빠른 시간 간격으로 스위칭 된다 ⇒ 여러 맥이 동시에 실행되는 것처럼 보인다 (concurrent vs simultaneous)
● 예: Web browser
– 화면 출력하는 쓰레드 + 데이터 읽어오는 쓰레드
● 예: Word processor
– 화면 출력하는 쓰레드 + 키보드 입력 받는 쓰레드 + 철자/문법
오류 확인 쓰레드
● 예: 음악 연주기, 동영상 플레이어, Eclipse IDE, …

  • 현대에는 대부분은 다중 쓰레드

Thread vs Process

한 프로세스에는 기본 1개의 쓰레드
– 단일 쓰레드 (single thread) 프로그램
● 한 프로세스에 여러 개의 쓰레드
– 다중 쓰레드 (multi-thread) 프로그램
● 쓰레드 구조
– 프로세스의 메모리 공간 공유 (code, data) – 프로세스의 자원 공유 (file, i/o, …) – 비공유: 개별적인 PC, SP, registers, stack(호출하는 메소드가 다를 수도 있고 안의 내용도 다르기 때문에)
● 프로세스의 스위칭 vs 쓰레드의 스위칭

예제: 자바 쓰레드

● 맥 만들기

  • 쓰레드 객체
    – java.lang.Thread
    – 주요 메소드
    ● public void run() // 새로운 맥이 흐르는 곳 (치환)
    ● void start() // 쓰레드 시작 요청
    ● void join() // 쓰레드가 마치기를 기다림
    ● static void sleep() // 쓰레드 잠자기

java.lang.Thread

• Thread.run()
– 쓰레드가 시작되면 run() 메소드가 실행된다
⇒ run() 메소드를 치환한다.

class MyThread extends Thread {
public void run() { // 치환 (override)
// 코드
}
}

• 예제: 글자 A 와 B 를 동시에 화면에 출력하기
– 모든 프로그램은 처음부터 1개의 쓰레드는 갖고 있다 (main) – 2개의 쓰레드: main + MyThread

프로세스 동기화

● Process Synchronization
– cf. Thread synchronization
● Processes
– Independent(프로세스 간의 관련이 없으면) vs. Cooperating(관련이 있다면)
– Cooperating process: one that can affect or be affected by
other processes executed in the system
– 프로세스간 통신: 전자우편, 파일 전송
– 프로세스간 자원 공유: 메모리 상의 자료들, 데이터베이스 등
– 명절 기차표 예약, 대학 온라인 수강신청, 실시간 주식거래

● Process Synchronization
Concurrent access to shared data may result in data
inconsistency

– Orderly execution of cooperating processes so that data
consistency is maintained = 순서를 지어 데이터 일관성을 유지할 수 있도록
● Example: BankAccount Problem (은행계좌문제)
– 부모님은 은행계좌에 입금; 자녀는 출금
– 입금(deposit)과 출금(withdraw) 은 독립적으로 일어난다.

class Test {
public static void main(String[] args)
throws InterruptedException {
BankAccount b = new
BankAccount();
Parent p = new Parent(b);
Child c = new Child(b);
p.start();
c.start();
p.join();
c.join();
System.out.println( "\nbalance = " + b.getBalance());
}
}

class BankAccount {
int balance;
void deposit(int amount) {
balance = balance + amount;
}
void withdraw(int amount) {
balance = balance - amount;
}
int getBalance() {
return balance;
}
}

class Parent extends Thread {
BankAccount b;
Parent(BankAccount b) {
this.b = b;
}
public void run() {
for (int i=0; i<100; i++)
b.deposit(1000);
}
}

class Child extends Thread {
BankAccount b;
Child(BankAccount b) {
this.b = b;
}
public void run() {
for (int i=0; i<100; i++)
b.withdraw(1000);
}
} 

BankAccount Problem

● 입출금 동작 알기 위해 "+", "-" 출력하기
● 입출금 동작에 시간 지연 추가
– 잘못된 결과값
– 이유: 공통변수(common variable)에 대한 동시 업데이트

  • 하이 레벨 언어에서는 한 줄이지만 로우 레벨 언어에서는 여러 줄이기 때문에
  • 스위칭이 같이 하고 타이밍이 다르기 때문에
    (concurrent update)
    – 해결: 한번(공동변수)에 한 쓰레드만 업데이트하도록 → 임계구역 문제

임계구역 문제

● The Critical-Section Problem

  • 이 구간에서 오류가 발생할 수 있음
    ● Critical section
    – A system consisting of multiple threads
    – Each thread has a segment of code(코드의 영역), called critical section, in
    which the thread may be changing common variables(공통 변수를 바꾸는 같이 사용하는 것),
    updating a table, writing a file, and so on.
    ● Solution
    – Mutual exclusion (상호배타): 오직 한 쓰레드만 진입(부모가 임계구역에 들어가면 자식은 들어가면 안됨)
    – Progress (진행): 진입 결정은 유한 시간 내(누가 먼저 들어갈것인지)
    – Bounded waiting (유한대기): 어느 쓰레드라도 유한 시간 내에 들어갈 수 있는지
    => 전부 다 만족해야 함.

프로세스/쓰레드 동기화

● 임계구역 문제 해결 (틀린 답이 나오지 않도록) -> 상호배타, 진행, 유한대기
● 프로세스 실행 순서 제어 (원하는대로)

동기화 도구

● Synchronization Tools
– Semaphores
– Monitors
– Misc.
● Semaphores (세마포)
– n. (철도의) 까치발 신호기, 시그널; U (군대의) 수기(手旗) 신호
– 동기화 문제 해결을 위한 소프트웨어 도구
– 네덜란드의 Edsger Dijkstra 가 제안
– 구조: 정수형 변수 + 두 개의 동작 (P, V)

세마포 (Semaphore)

● 동작
– P: Proberen (test) → acquire()
=> 호출값을 1씩 감소 -> 프로세스가 기다리는 리스트안에 넘어줌
– V: Verhogen (increment) → release()
=> 호출값을 1씩 증가 -> 값 체크 후, 큐에서 해방시켜줌
● 구조

class Semaphore {
int value; // number of permits
Semaphore(int value) { // 권한의 갯수
... }
void acquire() {
... }
void release() {
... }
}

void acquire() {
value--;
if (value < 0) {
add this process/thread to list;
block;
}
}

void acquire() {
value--;
if (value < 0) {
add this process/thread to list;
block;
}
}

void release() {
value++;
if (value <= 0) {
remove a process P from list;
wakeup P;
}
}

● 일반적 사용 (1): Mutual exclusion (상호배타 목적)
– sem.value = 1;
● 예제: BankAccount Problem
– java.util.concurrent.Semaphore
sem.acquire();

● 일반적 사용 (2): Ordering
– sem.value = 0;
● 예제: BankAccount Problem
– 항상 입금 먼저 (= Parent 먼저)
– 항상 출금 먼저 (= Child 먼저)
– 입출금 교대로 (P-C-P-C-P-C- …)
P1 P2

0개의 댓글