쓰레드(thread) : 프로그램 내부의 흐름 또는 맥
보통 하나의 프로그램은 하나의 흐름이 있다.
int main(void)
{
int n = 0;
int m = 6;
printf("%d", n + m);
while (n < m)
{
n++;
}
printf("Bye");
}
위와 같은 일반적인 프로그램은 프로그램 시작 시부터 종료 때까지 하나의 흐름이 있다. 이를 single thread라고 한다.
Multithread : 한 프로그램에 2개 이상의 쓰레드가 존재하는 것. 따라서 프로세스 내부의 2개 이상의 쓰레드가 동시에 작업을 수행하는 것을 의미한다.
하지만 우리는 하나의 cpu 내부에서는 한번에 하나의 작업만 수행할 수 있다고 배웠었다. 그렇다면 이 다중쓰레드에서는 이 법칙이 위배되어 실제로 프로세스 내부에 2개 이상의 쓰레드가 동시에 실행되는 것일까?
정답은 아니다. concurrent와 simultaneous를 비교하면서 이유를 분석해 보자.
< Concurrent vs Simultaneous >
Multithread도 thread가 빠른 시간 간격으로 스위칭되어 동시에 수행하는 것처럼 보이는 것이다. cpu가 1개인 이상 여러 연산을 동시에 수행할 수 없다.
multithread의 예) web browser(화면 출력 쓰레드 + 데이터 읽어오는 쓰레드), word processor(화면 출력하는 쓰레드 + 키보드 입력받는 쓰레드 + 철자/문법 오류 확인 쓰레드) 등
Single Thread Program : 하나의 프로세스에 하나의 쓰레드(기본적으로 프로세스는 하나의 쓰레드를 가진 싱글 쓰레드이다.)
Multi Thread Program : 하나의 프로세스에 여러 개의 쓰레드.
Context Switching
지금까지의 context switching은 프로세스 단위
였다면 이제는 thread 단위
로 switching된다. 예를 들어 p1의 쓰레드 1이 실행되었다면 context switching될 때는 p1의 쓰레드 2가 실행된다.
현대의 컴퓨터는 거의 다중 쓰레드를 가진다.
쓰레드의 구조
쓰레드끼리 공유 : 프로세스의 메모리 공간을 공유(code, data), 프로세스의 자원 공유 (file, i/o)
쓰레드끼리 비공유 : 개별적인 pc(쓰레드마다 다음에 실행할 명령어 번지인 pc가 다름), sp, registers, stack(return address나 parameter가 stack에 저장되어 stack까지 공유하면 x)
지금까지 쓰레드를 배운 이유 ? ==> 프로세스 관리 부서의 중요 업무가 cpu 스케줄링과 쓰레드 혹은 프로세스의 동기화이기 때문. 이제 프로세스의 동기화 부분을 살펴보자.
1 ) Independent : 두 프로세스 간 아무 관계가 없는 독립적인 프로세스.
2 ) Cooperating : 시스템 안에서 하나의 프로세스가 다른 하나에게 영향을 주거나 받는 프로세스.
< cooperating process의 예시 >
Process Synchronization(프로세스 동기화) : shared data에 대한 동시 접근은 data inconsistency를 발생시킴. 이를 막기 위해 프로세스 동기화가 필요.
shared data에 대한 동시 접근으로 인한 data inconsistency 발생의 예 : 은행계좌 문제. 부모는 입금, 자녀는 출금하는 예제.
아래는 은행계좌 문제의 자바 코드이다.
class Test
{
public static void main(String[] args)
throws InterruptedException
{
BankAccount b = new BankAccount();
Parent p = new Parnet(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 amoount)
{
balance = balance - amount;
}
int getBalancee()
{
return balance;
}
}
class Parent extends Thread
{
BankAccount b;
Parent(BankAccount b)
{
this.b = b;
}
public void run()
{
for (int i = 0; i < 1000; i++)
{
b.deposit(1000);
}
}
}
class Child extends Thread
{
BankAccount b;
Child(BankAccount b)
{
this.b = b;
}
public void run()
{
for (int i = 0; i < 1000; i++)
{
b.withdraw(1000);
}
}
}
자녀가 출금하는 쓰레드와 부모가 입금하는 쓰레드, 즉 멀티쓰레드로 프로그램 구성. 자녀와 부모가 같은 금액을 입금, 출금했는데 잔액이 잘못된 결과값이 나옴 ==> 임계구역 문제.