51
Design Pattern ohyecloudy http://ohyecloudy.com 아꿈사 http://cafe.naver.com/architect1.cafe 2010.6.12 Multithread

Multithread design pattern

  • Upload
    -

  • View
    3.858

  • Download
    4

Embed Size (px)

Citation preview

Page 1: Multithread design pattern

Design Pattern

ohyecloudy http://ohyecloudy.com

아꿈사 http://cafe.naver.com/architect1.cafe

2010.6.12

Multithread

Page 2: Multithread design pattern

기초적인 멀티쓰레드 디자인 패턴을 다룬 책

이 책에 나온 내용을 요약 발표

Page 3: Multithread design pattern

Single Threaded Execution 이 다리를 건널 수 있는 사람은 오직 한 명

Immutable

Guarded Suspension

Balking

Producer-Consumer

Read-Write Lock

Thread-Per-Message

Worker Thread

Future

C++::Boost

Page 4: Multithread design pattern

한 번에 한 개의 쓰레드 만이

처리를 실행할 수 있도록 제한

Page 5: Multithread design pattern

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()); } } }

Page 6: Multithread design pattern

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();

}

}

Page 7: Multithread design pattern

쓰레드 세이프thread-safe ?

쓰레드 여러개가 이용해도 안젂성safety이 유지?

쓰레드 여러개가 이용해도 객체를

망가뜨리지 않는가?

Page 8: Multithread design pattern

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”

Page 9: Multithread design pattern

쓰레드 세이프하게 맊들자.

동시에 한 개의 쓰레드에서만 실행되는 걸 보장

객체가 깨지는 것을 막는다.

synchronized keyword

Page 10: Multithread design pattern

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()); } } }

Page 11: Multithread design pattern

언제 사용하는가?

멀티 쓰레드 당연. 싱글 쓰레드는 필요 없다.

쓰레드 여러개가 접근access할 때

상태가 변화할 가능성이 있을 때

안정성을 확보할 필요가 있을 때

Page 12: Multithread design pattern

Single Threaded Execution

Immutable 망가뜨리고 싶어도 망가지지 않는다.

Guarded Suspension

Balking

Producer-Consumer

Read-Write Lock

Thread-Per-Message

Worker Thread

Future

C++::Boost

Page 13: Multithread design pattern

Immutable

불변의, 변하는 것이 없는

인스턴스 상태가 젃대 변하지 않는 클래스는

상호 배제mutual exclusion가 필요 없다.

Page 14: Multithread design pattern

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; } }

Page 15: Multithread design pattern

언제 사용하는가?

인스턴스 생성 후 상태가 변하지 않을 때

setter 메소드가 없다.

자주 접근하는 공유 인스턴스

상호 배제가 필요 없다는게 장점

빠르다는 이야기

Page 16: Multithread design pattern

조낸 접근 맋이 해서

Immutable 패턴을 사용하고 싶은데,

Setter가 있네요. 망했네요.

이런 경우는 죽어도

Immutable 패턴을 못 쓰나요?

Page 17: Multithread design pattern

Mutable 과 Immutable로

쪼갤 수 있는지 검토

String, StringBuffer가 좋은 예

Page 18: Multithread design pattern

Single Threaded Execution

Immutable

Guarded Suspension 준비가 될 때까지 기다려 주세요.

Balking

Producer-Consumer

Read-Write Lock

Thread-Per-Message

Worker Thread

Future

C++::Boost

Page 19: Multithread design pattern

Guarded

보호받고 있다.

Suspension

일시 정지함

처리해도 될 때까지 기다린다.

guarded wait, spin lock, busy wait

Page 20: Multithread design pattern

// 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(); } }

Page 21: Multithread design pattern

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(); ... } } }

Page 22: Multithread design pattern

Single Threaded Execution

Immutable

Guarded Suspension

Balking 필요 없으면 관둬요

Producer-Consumer

Read-Write Lock

Thread-Per-Message

Worker Thread

Future

C++::Boost

Page 23: Multithread design pattern

balk

중단하고 돌아가다

야구빠 : 생각하시는 그 보크 맞습니다.

가드 조건

Guarded Suspension 패턴에 등장

단, 기다리지 않고 바로 중단한다.

Page 24: Multithread design pattern

public class Data {

...

public synchronized void change(String newContent) {

content = newContent;

changed = true;

}

public synchronized void save() {

if (!changed) {

return;

}

...

}

}

Page 25: Multithread design pattern

언제 사용하는가?

굳이 처리할 필요가 없는 경우

가드 조건이 충족되기를

기다리고 싶지 않은 경우

가드 조건을 맊족하는 것이 처음 1회뿐인 경우

예) 초기화

Page 26: Multithread design pattern

Single Threaded Execution

Immutable

Guarded Suspension

Balking

Producer-Consumer 내가 만들고 당신이 사용한다.

Read-Write Lock

Thread-Per-Message

Worker Thread

Future

C++::Boost

Page 27: Multithread design pattern

producer

생산자 : 데이터를 작성하는 쓰레드

consumer

소비자 : 데이터를 이용하는 쓰레드

channel

생산자와 소비자를 이어주는 중개 역할.

생산자와 소비자 처리 속도 차이를 메운다.

상호 배제는 여기에서만 수행

Page 28: Multithread design pattern

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()); } } }

Page 29: Multithread design pattern

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; } }

Page 30: Multithread design pattern

Single Threaded Execution

Immutable

Guarded Suspension

Balking

Producer-Consumer

Read-Write Lock 다 같이 읽는 것은 상관없지만 읽는 중간에 쓰면 안돼요.

Thread-Per-Message

Worker Thread

Future

C++::Boost

Page 31: Multithread design pattern

읽기 : 읽기 쓰레드 여러개 접근 문제 없다.

쓰기 : 인스턴스 상태가 바뀜

다른 쓰레드가 읽던가 쓰던가 다 문제 생김.

상호 배제를 나눠서 하자.

읽기 상호 배제

쓰기 상호 배제

Page 32: Multithread design pattern

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(); } } }

Page 33: Multithread design pattern

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();

}

Page 34: Multithread design pattern

public synchronized void writerLock() {

waitingWriters++;

try {

while (readingReaders > 0 || writingWriters > 0) {

wait();

}

} finally {

waitingWriters--;

}

writingWriters++;

}

public synchronized void writerUnlock() {

writingWriters--;

preferWriter = false;

notifyAll();

}

}

Page 35: Multithread design pattern

언제 사용하는가?

읽기 빈도가 쓰기 빈도보다 높을 때

읽기 처리가 무거울 때

Page 36: Multithread design pattern

Single Threaded Execution

Immutable

Guarded Suspension

Balking

Producer-Consumer

Read-Write Lock

Thread-Per-Message 이 일을 부탁해요

Worker Thread

Future

C++::Boost

Page 37: Multithread design pattern

명령이나 요구마다

새로 만든 쓰레드 한 개가 할당

그 쓰레드가 처리

실행 순서 보장하지 않는다.

실행 결과를 받아보지 못한다.

Page 38: Multithread design pattern

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’); } }

Page 39: Multithread design pattern

Single Threaded Execution

Immutable

Guarded Suspension

Balking

Producer-Consumer

Read-Write Lock

Thread-Per-Message

Worker Thread 일이 올 때까지 기다리고, 일이 오면 작업한다.

Future

C++::Boost

Page 40: Multithread design pattern

일을 처리할 수 있는 쓰레드를 미리 맊듬 Worker Thread

신나게 굴린다. 일이 없을 때맊 쉬고 일이 주어지면

놀고 있는 쓰레드가 처리.

Thread-Per-Message 패턴에서

새로운 thread를 매번 생성하는 부담을

덜어주는 패턴

Thread Pool이라고도 불림

Page 41: Multithread design pattern

// 리퀘스트를 받아서 전달. 워커 쓰레드를 보유. 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; } }

Page 42: Multithread design pattern

public class WorkerThread extends Thread {

private final Channel channel;

...

public void run() {

while (true) {

Request request = channel.takeRequest();

request.execute();

}

}

}

Page 43: Multithread design pattern

Single Threaded Execution

Immutable

Guarded Suspension

Balking

Producer-Consumer

Read-Write Lock

Thread-Per-Message

Worker Thread

Future 먼저 교환권을 받으세요

C++::Boost

Page 44: Multithread design pattern

Future

미래

경제 용어) 선물

실행 결과를 구할 때까지 기다리는 대신에

바로 교홖권을 받는다.

교환권으로 실행 결과를 받음.

아직 결과가 안 나왔다면 기다린다.

Thread-Per-Message, Worker Thread에서 실행 결과가 필요할 때 사용.

Page 45: Multithread design pattern

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;

}

}

Page 46: Multithread design pattern

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(); } }

Page 47: Multithread design pattern

public class ReadData {

...

public RealData(int count, char c) {

// 실제 결과물을 계산한다.

// 시간이 걸리는 작업

this.content = new String(buffer);

}

public String getContent() {

return content();

}

}

Page 48: Multithread design pattern

Single Threaded Execution

Immutable

Guarded Suspension

Balking

Producer-Consumer

Read-Write Lock

Thread-Per-Message

Worker Thread

Future

C++::Boost

Page 49: Multithread design pattern

Java 좋다.

다 구현돼 있네.

C++에서 패턴에맊 집중해서

편하게 써보고 싶다면

Boost를 사용하면 된다.

Page 50: Multithread design pattern

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;

};

Page 51: Multithread design pattern