multithread design pattern
Post on 24-Jun-2015
3.858 Views
Preview:
TRANSCRIPT
Design Pattern
ohyecloudy http://ohyecloudy.com
아꿈사 http://cafe.naver.com/architect1.cafe
2010.6.12
Multithread
기초적인 멀티쓰레드 디자인 패턴을 다룬 책
이 책에 나온 내용을 요약 발표
Single Threaded Execution 이 다리를 건널 수 있는 사람은 오직 한 명
Immutable
Guarded Suspension
Balking
Producer-Consumer
Read-Write Lock
Thread-Per-Message
Worker Thread
Future
C++::Boost
한 번에 한 개의 쓰레드 만이
처리를 실행할 수 있도록 제한
public class Gate { private int counter = 0; private String name = “Nobody”; private String address = “Nowhere”; public void pass(String name, String address) { this.counter++; this.name = name; this.address = address; check(); } public String toString() { return “No.” + counter + “: “ + name + “, “ + address; } private void check() { if (name.charAt(0) != address.charAt(0)) { System.out.println(“*****BROKEN*****” + toString()); } } }
public class UserThread extends Thread {
...
public void run() {
while(thue) {
gate.pass(myname, myaddress);
}
}
}
public class Main {
public static void main(String[] args) {
Gate gate = new Gate();
new UserThread(gate, “Alice”, “Alaska”).start();
new UserThread(gate, “Bobby”, “Brazil”).start();
new UserThread(gate, “Chris”, “Canada”).start();
}
}
쓰레드 세이프thread-safe ?
쓰레드 여러개가 이용해도 안젂성safety이 유지?
쓰레드 여러개가 이용해도 객체를
망가뜨리지 않는가?
public class Gate { private int counter = 0; private String name = “Nobody”; private String address = “Nowhere”; public void pass(String name, String address) { this.counter++; this.name = name; this.address = address; check(); } public String toString() { return “No.” + counter + “: “ + name + “, “ + address; } private void check() { if (name.charAt(0) != address.charAt(0)) { System.out.println(“*****BROKEN*****” + toString()); } } }
1. Alice 쓰레드가 여기까지 짂행
2. Bobby 쓰레드가 여기까지 짂행
3. Alice 쓰레드가 다시 짂행
4. name = “Bobby”, address=“Alaska”
쓰레드 세이프하게 맊들자.
동시에 한 개의 쓰레드에서만 실행되는 걸 보장
객체가 깨지는 것을 막는다.
synchronized keyword
public class Gate { private int counter = 0; private String name = “Nobody”; private String address = “Nowhere”; public synchronized void pass( String name, String address) { this.counter++; this.name = name; this.address = address; check(); } public synchronized String toString() { return “No.” + counter + “: “ + name + “, “ + address; } private void check() { if (name.charAt(0) != address.charAt(0)) { System.out.println(“*****BROKEN*****” + toString()); } } }
언제 사용하는가?
멀티 쓰레드 당연. 싱글 쓰레드는 필요 없다.
쓰레드 여러개가 접근access할 때
상태가 변화할 가능성이 있을 때
안정성을 확보할 필요가 있을 때
Single Threaded Execution
Immutable 망가뜨리고 싶어도 망가지지 않는다.
Guarded Suspension
Balking
Producer-Consumer
Read-Write Lock
Thread-Per-Message
Worker Thread
Future
C++::Boost
Immutable
불변의, 변하는 것이 없는
인스턴스 상태가 젃대 변하지 않는 클래스는
상호 배제mutual exclusion가 필요 없다.
public final class Person { private final String name; private final String address; public Person(String name, String address) { this.name = name; this.address = address; } public String getName() { return name; } public String getAddress() { return address; } public String toString() { return name + “ , “ + address; } }
언제 사용하는가?
인스턴스 생성 후 상태가 변하지 않을 때
setter 메소드가 없다.
자주 접근하는 공유 인스턴스
상호 배제가 필요 없다는게 장점
빠르다는 이야기
조낸 접근 맋이 해서
Immutable 패턴을 사용하고 싶은데,
Setter가 있네요. 망했네요.
이런 경우는 죽어도
Immutable 패턴을 못 쓰나요?
Mutable 과 Immutable로
쪼갤 수 있는지 검토
String, StringBuffer가 좋은 예
Single Threaded Execution
Immutable
Guarded Suspension 준비가 될 때까지 기다려 주세요.
Balking
Producer-Consumer
Read-Write Lock
Thread-Per-Message
Worker Thread
Future
C++::Boost
Guarded
보호받고 있다.
Suspension
일시 정지함
처리해도 될 때까지 기다린다.
guarded wait, spin lock, busy wait
// immutable public class Request { private final String name; ... public String getName() {...} public String toString() {...} } public class RequestQueue { public synchronized Request getRequest() { while (queue.peek() == null) { try { wait(); } catch (InterruptedException e) { } } return queue.remove(); } public synchronized void putRequest(Request request) { queue.offer(request); notifyAll(); } }
public class ClientThread extends Thread { ... public void run() { while (true) { ... requestQueue.putRequest(request); } } } public class ServerThread extends Thread { ... public void run() { while (true) { Request request = requestQueue.getRequest(); ... } } }
Single Threaded Execution
Immutable
Guarded Suspension
Balking 필요 없으면 관둬요
Producer-Consumer
Read-Write Lock
Thread-Per-Message
Worker Thread
Future
C++::Boost
balk
중단하고 돌아가다
야구빠 : 생각하시는 그 보크 맞습니다.
가드 조건
Guarded Suspension 패턴에 등장
단, 기다리지 않고 바로 중단한다.
public class Data {
...
public synchronized void change(String newContent) {
content = newContent;
changed = true;
}
public synchronized void save() {
if (!changed) {
return;
}
...
}
}
언제 사용하는가?
굳이 처리할 필요가 없는 경우
가드 조건이 충족되기를
기다리고 싶지 않은 경우
가드 조건을 맊족하는 것이 처음 1회뿐인 경우
예) 초기화
Single Threaded Execution
Immutable
Guarded Suspension
Balking
Producer-Consumer 내가 만들고 당신이 사용한다.
Read-Write Lock
Thread-Per-Message
Worker Thread
Future
C++::Boost
producer
생산자 : 데이터를 작성하는 쓰레드
consumer
소비자 : 데이터를 이용하는 쓰레드
channel
생산자와 소비자를 이어주는 중개 역할.
생산자와 소비자 처리 속도 차이를 메운다.
상호 배제는 여기에서만 수행
public class MakerThread extends Thread { ... public void run() { while (true) { Thread.sleep(GetRandom()); table.put(cake); } } } public class EaterThread extends Thread { ... public void run() { while (true) { String cake = table.take(); Thread.sleep(GetRandom()); } } }
public class Table { ... public synchronized void put(String cake) { while (count >= buffer.length) { wait(); } // 케이크를 놓는다. notifyAll(); } public synchronized String take() { while (count <= 0) { wait(); } // 죿 케이크를 죾비 notifyAll(); return cake; } }
Single Threaded Execution
Immutable
Guarded Suspension
Balking
Producer-Consumer
Read-Write Lock 다 같이 읽는 것은 상관없지만 읽는 중간에 쓰면 안돼요.
Thread-Per-Message
Worker Thread
Future
C++::Boost
읽기 : 읽기 쓰레드 여러개 접근 문제 없다.
쓰기 : 인스턴스 상태가 바뀜
다른 쓰레드가 읽던가 쓰던가 다 문제 생김.
상호 배제를 나눠서 하자.
읽기 상호 배제
쓰기 상호 배제
public class Data { ... public char[] read() { lock.readLock(); try { return doRead(); } finally { lock.readUnlock(); } } public void write(char c) { lock.writeLock(); try { doWrite(c); } finally { lock.writeUnlock(); } } }
public final class ReadWriteLock {
private int readingReaders = 0;
private int waitingWriters = 0;
// 한번에 한 쓰레드맊 쓰기가 가능하기 때문에 대기자를 기록.
private int writingWriters = 0;
// 읽기, 쓰기 한쪽이 굶어 죽는 것을 방지.
private boolean preferWriter = true;
public synchronized void readLock() {
// 쓰기 락이 걸렸거나 기다리는 쓰기 락을 걸어줘야 할 때.
while (writingWriters > 0 ||
(preferWriter && waitingWriters > 0)) {
wait();
}
readingReaders++;
}
public synchronized void readUnlock() {
readingReaders--;
preferWriter = true;
notifyAll();
}
public synchronized void writerLock() {
waitingWriters++;
try {
while (readingReaders > 0 || writingWriters > 0) {
wait();
}
} finally {
waitingWriters--;
}
writingWriters++;
}
public synchronized void writerUnlock() {
writingWriters--;
preferWriter = false;
notifyAll();
}
}
언제 사용하는가?
읽기 빈도가 쓰기 빈도보다 높을 때
읽기 처리가 무거울 때
Single Threaded Execution
Immutable
Guarded Suspension
Balking
Producer-Consumer
Read-Write Lock
Thread-Per-Message 이 일을 부탁해요
Worker Thread
Future
C++::Boost
명령이나 요구마다
새로 만든 쓰레드 한 개가 할당
그 쓰레드가 처리
실행 순서 보장하지 않는다.
실행 결과를 받아보지 못한다.
public class Host { public void request( final int count, final char c) { new Thread() { public void run() { helper.handle(count, c); } }.start(); } } public class Main { public static void Main(String[] args) { Host host = new Host(); host.request(10, ‘A’); host.request(20, ‘B’); host.request(30, ‘C’); } }
Single Threaded Execution
Immutable
Guarded Suspension
Balking
Producer-Consumer
Read-Write Lock
Thread-Per-Message
Worker Thread 일이 올 때까지 기다리고, 일이 오면 작업한다.
Future
C++::Boost
일을 처리할 수 있는 쓰레드를 미리 맊듬 Worker Thread
신나게 굴린다. 일이 없을 때맊 쉬고 일이 주어지면
놀고 있는 쓰레드가 처리.
Thread-Per-Message 패턴에서
새로운 thread를 매번 생성하는 부담을
덜어주는 패턴
Thread Pool이라고도 불림
// 리퀘스트를 받아서 전달. 워커 쓰레드를 보유. public class Channel { ... public void startWorkers() { for (int i = 0; i < threadPool.length; ++i) { threadPool[i].start(); } } public synchronized void putRequest(Request request) { while (count >= requestQueue.length) { wait(); } // 리퀘스트를 저장 notifyAll(); } public synchronized Request takeRequest() { while (count <= 0) { wait(); } // 큐에서 리퀘스트를 가져옴. notifyAll(); return request; } }
public class WorkerThread extends Thread {
private final Channel channel;
...
public void run() {
while (true) {
Request request = channel.takeRequest();
request.execute();
}
}
}
Single Threaded Execution
Immutable
Guarded Suspension
Balking
Producer-Consumer
Read-Write Lock
Thread-Per-Message
Worker Thread
Future 먼저 교환권을 받으세요
C++::Boost
Future
미래
경제 용어) 선물
실행 결과를 구할 때까지 기다리는 대신에
바로 교홖권을 받는다.
교환권으로 실행 결과를 받음.
아직 결과가 안 나왔다면 기다린다.
Thread-Per-Message, Worker Thread에서 실행 결과가 필요할 때 사용.
public class Host {
public Data request(final int count, final char c) {
final FutureData future = new FutureData();
new Thread() {
public void run() {
RealData realData = new RealData(count, c);
future.setRealData(realData);
}
}.start();
return future;
}
}
public class FutureData { ... public synchronized void setRealData( RealData realdata) { if (ready) { return; // balk } this.realdata = realdata; this.ready = true; notifyAll(); } public synchronized String getContent() { while (!ready) { wait(); } return realdata.getContent(); } }
public class ReadData {
...
public RealData(int count, char c) {
// 실제 결과물을 계산한다.
// 시간이 걸리는 작업
this.content = new String(buffer);
}
public String getContent() {
return content();
}
}
Single Threaded Execution
Immutable
Guarded Suspension
Balking
Producer-Consumer
Read-Write Lock
Thread-Per-Message
Worker Thread
Future
C++::Boost
Java 좋다.
다 구현돼 있네.
C++에서 패턴에맊 집중해서
편하게 써보고 싶다면
Boost를 사용하면 된다.
class ReadWriteLock
{
public:
void ReadLock()
{
boost::mutex::scoped_lock lock(m_mutex);
{
while (m_writingWriters > 0 || (m_preferWriter && m_waitingWriters > 0))
{
m_cond.wait(lock);
}
m_readingReaders++;
}
}
void ReadUnlock()
{
boost::mutex::scoped_lock lock(m_mutex);
{
m_readingReaders--;
m_preferWriter = true;
m_cond.notify_all();
}
}
...
private:
boost::mutex m_mutex;
boost::condition_variable m_cond;
};
top related