목적
- CPU 이용의 기본 단위가 된 스레드를 이해한다.
실천 목표
- 스레드의 기본 구성요소를 식별하고 스레드와 프로세스를 대조한다.
- 다중 스레드 프로세스를 설계할 때의 주요 이점과 중대한 과제를 설명한다.
이제까지의 내용은 사실, 싱글 스레드
- 프로세스 내 스레드가 1개인 싱글 스레드
그렇다면 스레드란
- 프로세스 내에서 특정 부분을 공유하며 별도로 수행되는 일꾼
- 경량 프로세스라고 할 수 있으며, CPU를 점유하는 기본적인 단위
- 한 프로세스 내 스레드가 공유하는 변수들 : code, data, files
- 스레드 별로 할당되는 변수들 : TID(Thread ID), PC(Program Counter), Register Set, Stack
멀티 스레드의 이점
- 기존 싱글 스레드
- [참고] 기존 싱글 스레드 : Data Server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
package os.concepts; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class DateServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; int port = 7777; try { /* 서버 소켓 생성 후 7777번 포트와 결합 */ serverSocket = new ServerSocket(port); System.out.printf("%s 서버가 준비되었습니다.\n", getTime()); } catch (Exception e) { e.printStackTrace(); } while (true) { System.out.printf("%s 연결요청을 기다립니다.\n", getTime()); /* 서버 소켓은 클라이언트의 연결요청이 올 때까지 실행을 멈추고 기다림 */ /* 클라이언트의 연결요청이 들어오면 클라이언트 소켓과 통신할 새로운 소켓 생성 */ Socket socket = serverSocket.accept(); System.out.printf("%s %s:%d으로부터 연결요청이 들어왔습니다.\n", getTime(), socket.getInetAddress(), socket.getPort()); /* 소켓의 출력스트림을 얻음 */ OutputStream out = socket.getOutputStream(); PrintWriter pout = new PrintWriter(out, true); /* 원격 소켓에 데이터를 보냄 */ pout.printf("%s Message From Server\n", getTime()); /* 스트림과 소켓을 닫음 */ socket.close(); pout.close(); out.close(); } } private static String getTime() { DateFormat df = new SimpleDateFormat("[hh:mm:ss]"); return df.format(new Date()); } }
- [참고] 기존 싱글 스레드 : Data Client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
package os.concepts; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ConnectException; import java.net.Socket; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class DateClient { public static void main(String[] args) { try { String serverIp = "127.0.0.1"; int serverPort = 7777; System.out.printf("%s:%d서버에 연결 중입니다.\n", serverIp, serverPort); /* 소켓을 생성해서 연결을 요청 */ Socket socket = new Socket(serverIp, serverPort); /* 소켓의 입력스트림을 얻음 */ InputStream in = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); /* 원격 소켓으로부터 받은 데이터 출력 */ System.out.printf("%s 서버로부터 받은 메시지\n", getTime()); String line = null; while ((line = br.readLine()) != null) System.out.println(line); /* 스트림과 소켓을 닫음 */ socket.close(); br.close(); in.close(); } catch (ConnectException ce) { ce.printStackTrace(); } catch (IOException ie) { ie.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } private static String getTime() { DateFormat df = new SimpleDateFormat("[hh:mm:ss]"); return df.format(new Date()); } }
- 웹 서버의 서버-클라이언트 시스템이 멀티 스레드의 대표적인 예 Copyrightⓒ2022 <CentOS 7으로 리눅스마스터 1급 정복하기>, 정성재, 북스홀릭 All rights reserved. Copyrightⓒ2022 <CentOS 7으로 리눅스마스터 1급 정복하기>, 정성재, 북스홀릭 All rights reserved.
- 그래서, 멀티 스레드의 이점 4가지
- 응답성 (Responsiveness)
- 특히 사용자 인터페이스에서 중요
- 자원 공유 (Resource Sharing)
- 스레드는 자동으로 그들이 속한 프로세스의 자원들과 메모리를 공유
- 경제성 (Economy)
- 프로세스보다 스레드를 생성하고 문맥 교환하는 게 경제적
- 규모 적응성 (Scalability)
- 멀티 코어 구조에서 멀티 스레드의 이점이 더욱 증가
- 응답성 (Responsiveness)
스레드 프로그래밍 (Java)
- Java에서는 프로그램 수행의 기본적인 모델이 스레드 _Copyrightⓒ2022
, 남궁성, 도우출판 All rights reserved._ - 스레드 라이브러리를 이용하는 방법은 기본적으로 2가지
- Thread 클래스 상속 받기
- Runnable 인터페이스 구현하기
- 불필요하게 클래스를 하나 생성해야 하므로 람다 표현식 이용해서 익명 클래스로 대체 가능
- Thread 클래스 상속 받기
- Runnable 인터페이스 구현하기
- Runnable 인터페이스 구현하기 - 람다 표현식 이용해서 익명 클래스로 대체
- 불필요하게 클래스를 하나 생성해야 하므로 람다 표현식 이용해서 익명 클래스로 대체 가능
- 부모 스레드의 대기와 스레드의 종료
멀티 코어 프로그래밍
- 멀티 스레드는 멀티 코어 시스템에서 보다 효율적
- 시분할(Time-sharing) 뿐 아니라 병렬(Parallelism)로 실행되기 때문
- 그러나 멀티 코어 프로그래밍을 하는 것은 복잡한 작업이고, 다음은 이에 대한 5가지 도전 과제
- 태스크 인식 (identifying tasks)
- 응용 프로그램을 분석해서 독립된 병행 가능 태스크로 나눌 수 있는 영역을 찾는 작업
- 균형 (balance)
- 찾아낸 영역들이 균등한 기여도를 가지도록 태스크를 나누는 작업
- 데이터 분리 (data spliting)
- 태스크가 접근하고 조작하는 데이터를 개별 코어에서 사용할 수 있도록 나누는 작업
- 데이터 종속성 (data dependency)
- 태스크가 접근하는 데이터에 대해 둘 이상의 태스크 사이에 종속성이 없는지 검토
- 테스트 및 디버깅 (test and debugging)
- 단일 스레드인 경우보다 테스트 및 디버깅이 어려움
- 태스크 인식 (identifying tasks)
- 위 문제만 해결한다면, 멀티 코어 시스템에서 코어는 다다익선인가?
참고
- 운영체제 공룡책 강의 | 주니온 | 인프런 https://www.inflearn.com/course/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EA%B3%B5%EB%A3%A1%EC%B1%85-%EC%A0%84%EA%B3%B5%EA%B0%95%EC%9D%98/dashboard
운영체제 제 10판 Abraham Silberschatz, Peter Baer Galvin, Greg Gagne 저/박민규 역 퍼스트북 2020년 02월 28일 - 운영체제 제 10판 솔루션 https://codex.cs.yale.edu/avi/os-book/OS10/practice-exercises/index-solu.html