쓰레드 라이브러리?
프로그래머에게 쓰레드를 생성하고 관리하기 위한 API를 제공.
제공 방법에는 두 가지가 존재한다.
-
사용자 공간에서만 라이브러리를 제공하는 것.
모든 코드와 자료구조는 사용자 공간에서만 존재.
-
운영체제에 의해 지원되는 커널 수준 라이브러리를 구현.
이건 결국 시스템 콜을 부르는 것으로 볼 수 있다.
쓰레드 생성기법
- 비동기 쓰레딩
- 부모 쓰레드가 자식 쓰레드를 낳는다.
- 이후 부모 쓰레드가 자식 쓰레드의 종류까지 기다린다.
- 두 쓰레드가 병렬적으로 실행. 데이터 공유 안 함.
- 동기 쓰레딩
- 부모 쓰레드가 자식 쓰레드를 낳는다.
- 이후 부모 쓰레드가 자식 쓰레드의 종류까지 기다린다.
- 부모 쓰레드와 자식 쓰레드 사이에 상당한 데이터가 공유된다.
(여기서는 동기 쓰레딩으로 예시를 들음)
유닉스 : Pthreads
c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
int sum;
int main(int ac, char **av)
{
pthread_t tid; // 쓰레드 식별자 (자식 쓰레드를 의미)
pthread_attr_t attr; // 쓰레드 속성
pthread_attr_init(&attr); // 쓰레드 속성 지정
pthread_create(&tid, &attr, runner, av[1]); // 쓰레드 실행
pthread_join(tid, NULL); // 쓰레드 tid가 반환될 때까지 기다림.
printf("sum = %d\n", sum); // 결과 출력
}
void *runner(void *param)
{
int upper = atoi(param);
sum = 0;
for (int i = 1; i <= upper; i++)
sum += i;
pthread_exit(0); // 쓰레드 종료
}
윈도우
c
#include <windows.h>
#include <stdio.h>
// DWORD = unsigned int (32비트)
DWORD sum;
DWORD WINAPI Summation(LPVOID Param)
{
DWORD upper = *(DWORD *)param;
for (DWORD i = 1; i < Upper; i++)
sum += i;
return (0);
}
int main(int ac, char **av)
{
DWORD ThreadId; // 쓰레드 식별자
HANDLE ThreadHandle; // 쓰레드 핸들러
int Param = atoi(av[1]);
// 쓰레드 생성
ThreadHandle = CreateThread(NULL, 0, Summation, &Param, 0, &ThreadId);
/*
NULL = 보안을 위한 값
0 = 기본 스택 사이즈
Summation = 쓰레드가 실행시킬 함수
Param = 함수에 넣을 파라미터
0 = 기본 플래그
ThreadID = 쓰레드 식별자 반환
*/
WaitForSingleObject(ThreadHandle, INFINITE);
// ThreadHandle을 무한(INFINITE)시간 동안 기다린다는 뜻.
CloseHandle(ThreadHandle);
printf("sum = %d\n", sum);
}
자바
만드는 법 (1)
java
class Task implements Runnable
{
// 쓰레드로 돌아갈 클래스 작성하기
// 인터페이스 Runnable을 상속받는다.
@override
public void run() {
// 상속받은 run()을 재정의 : 오버라이딩
// 원래 Runnable 클래스에 있던 애를 수정하는 작업
System.out.println("안녕");
}
}
public class thread
{
public static void main(String args[]) {
/* 쓰레드로 돌아갈 함수가 담긴 객체를 만든다. */
Runnable run1 = new Task();
// 쓰레드 객체를 만들고, 생성자에 함수가 담긴 객체를 넣는다.
Thread worker = new Thread(run1);
// run1의 run함수를 대신해줄 start함수를 실행한다.
// 이렇게 하면 자식 쓰레드가 탄생한다.
worker.start();
// 자식 쓰레드를 기다린다.
try {
worker.join();
}
catch(InterruptedException ie) {
System.out.println("안 돌아옴.");
}
}
}
만드는 법 (2)
좀 더 최신 방식.
java
import java.util.concurrent.*;
class Sum implements Callable<Integer>
{
private int upper;
// 생성자 오버로딩
public Sum (int upper) {
this.upper = upper;
}
@override
public Integer call() {
// 상속받은 call()을 재정의
// Runnable의 run()과 달리, 반환값이 있다.
int sum = 0;
for (int i = 0; i < upper; i++)
sum += i;
return new Integer(sum);
}
}
public class thread {
public static void main(String args[]) {
int upper = Integer.parseInt(args[0]);
// 쓰레드를 만든다. (쓰레드 풀 이용)
ExecutorService pool = Executors.newSingleThreadExecutor();
// 클래스가 실행시킬 함수가 담긴 객체를 생성한다.
Sum sum = new Sum(upper);
// Callable 타입의 객체는 항상 Future 타입의 객체를 반환한다.
// Future 객체 속 result가 자식 프로세스가 실행시킨 함수의 값이다.
Future<Integer> result = pool.submit(sum);
// 자식 쓰레드 반환을 기다린다.
try {
System.out.println("sum = " + result.get());
}
catch (InterruptedException | ExecutionException ie) {
System.out.println("안 돌아옴.");
}
}
}