60
시즌 2 : 멀티쓰레드 프로그래밍이 왜이리 힘드나요? (Lock-free에서 Transactional Memory까지) 정내훈 한국산업기술대학교 게임공학과

시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

  • Upload
    zzapuno

  • View
    10.967

  • Download
    1

Embed Size (px)

DESCRIPTION

Lock-free 멀티쓰레드 프로그래밍 기법과 Transactional Memory를 실제 사용예제를 통해 설명하고, 성능을 보여줌.

Citation preview

Page 1: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

시즌 2 : 멀티쓰레드 프로그래밍이

왜이리 힘드나요?

(Lock-free에서 Transactional Memory까지)

정내훈

한국산업기술대학교 게임공학과

Page 2: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

발표자 소개

KAIST 전산과 박사

− 전공 : 멀티프로세서 CPU용 일관성 유지 HW

NCSoft 근무

− Alterlife 프로그램 팀장

− Project M(현 Blade & Soul) 프로그램 팀장

− CTO 직속 게임기술연구팀

현 : 한국산업기술대학교 게임공학과 부교수

− 학부 강의 : 게임서버프로그래밍

− 대학원 강의 : 멀티코어프로그래밍, 심화 게임서버

프로그래밍

2-2

Page 3: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

참고

삼성 첨단기술연수소에서 강의한 내용 반영

− 40시간 강의 (실습 포함) => 뒷부분 만 발췌

대학원 4주 강의 분량의 압축

2-3

Page 4: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

2-4

목차

도입 : 그래도 멀티쓰레드 프로그램을 하시려구요?

대책

성능

미래 또는 현실

Page 5: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

도입

제가 하는 발표 들어본 적이 있으신 분?

멀티쓰레드 프로그래밍 경험 있으신 분?

Lock-free 자료 구조가 무엇인지 아시는 분?

2-5

CJ E&M, NDC, KGC2012, 삼성, JCE

Page 6: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

도입

멀티쓰레드 프로그래밍의 위험성

− “자꾸 죽는데 이유를 모르겠어요”

자매품 : “이상한 값이 나오는데 이유를 모르겠어요”

− “더 느려져요”

2-6

[미] MuliThreadProgramming [mʌ́ltiθred-|proʊgrӕmɪŋ] : 1. 흑마술, 마공

2. 위력이 강대하나 다루기 어려워 잘 쓰이지 않는 기술

Page 7: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

도입

멀티쓰레드 프로그래밍의 어려움 (한페이지 요약)

− Data Race : 2를 5천만번 더했는데 1억이 안 나오는

경우.

Lock을 사용해 해결

− 컴파일러 : 변수를 참조했는데, 컴파일러가 무시

volatile 키워드로 해결

− CPU : 프로그램을 자기 마음대로 변조

asm mfence; 명령으로 해결

− Cache : -1을 썼는데 65535가 써짐

포인터 주소 확인하기.

− 성능 : 싱글 쓰레드 버전보다 더 느림

Lock 쓰지 말자.

2-7

ABA

문제는?

Page 8: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

목차

도입

대책 : Lock-free 프로그래밍이 도대체

뭐야?

성능

미래 또는 현실

2-8

Page 9: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

현실의 멀티쓰레드 프로그램은?

− 여러 쓰레드가 동시에 멀티 코어에서 실행된다.

− 쓰레드간의 데이터 공유 및 동기화는 안전한

Lock-free 자료구조를 통해서 이루어진다.

− 언리얼 3 : 디스플레이 리스트 Queue

− 각종 게임 서버 : Job Queue외 다수

2-9

Page 10: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Lock-free 알고리즘을 사용하여야 한다.

사용하지 않으면

− 병렬성 감소

− Priority Inversion

− Convoying

− /* 성능이 떨어지고 랙이 발생한다 */

− /* 작년에 보여드렸어요~~ */

2-

10

Page 11: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Lock-free 알고리즘이란?

− 여러 개의 쓰레드에서 동시에 호출했을 때에도

정해진 단위 시간마다 적어도 한 개의 호출이

완료되는 알고리즘.

2-

11

??????

Page 12: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Lock-free 알고리즘이란?

− 자료구조 및 그것에 대한 접근 방법

예) QUEUE : enqueue, dequeue

예) STACK : push, pop

예) 이진 트리 : insert, delete, search

2-

12

Page 13: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Lock-free 알고리즘이란?

− 멀티쓰레드에서 동시에 호출해도 정확한

결과를 만들어 주는 알고리즘

STL 탈락.

− Non-Blocking 알고리즘

다른 쓰레드가 어떤 상태에 있건 상관없이 호출이

완료된다.

− 호출이 다른 쓰레드와 충돌하였을 경우 적어도

하나의 승자가 있어서, 승자는 delay없이 완료

된다.

2-

13

Page 14: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

(보너스)

− Wait-free 알고리즘은?

호출이 다른 쓰레드와 충돌해도 모두 delay없이

완료 된다.

추가 상식

− LOCK을 사용하지 않는다고 lock-free

알고리즘이 아니다!!!

− LOCK을 사용하면 무조건 lock-free알고리즘이

아니다.

2-

14

Page 15: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

알고리즘의 분류

2-

15

알고리즘

싱글쓰레드 멀티쓰레드

Blocking Non-blocking

Lock-free

Wait-free

….

….

Page 16: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

예) Blocking 알고리즘

2-

16

EnterCriticalSection(&mylock);

sum = sum + 2;

LeaveCriticalSection(&mylock);

EnterCriticalSection(&mylock);

q.push(35);

LeaveCriticalSection(&mylock);

while (dataReady == false);

asm mfence;

temp = g_data;

Page 17: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

왜 Blocking인가?

− dataReady에 true가 들어가지 않으면 이

알고리즘은 무한 대기, 즉 다른 쓰레드에서

무언가 해주기를 기다린다.

− 여러 가지 이유로 dataReady에 true가

들어오는 것이 지연될 수 있다.

Schedule out, 다른 쓰레드 때문에 대기

2-

17

while (dataReady == false);

temp = g_data;

Page 18: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Non-blocking은?

2-

18

InterlockedExchangeAdd(&sum, 2);

q.push(35);

LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL == next) {

if (CAS(&(last->next), NULL, e)) {

CAS(&tail, last, e);

return;

}

} else CAS(&tail, last, next);

} }

Page 19: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Non-blocking은?

2-

19

if (dataReady == false) return false;

asm mfence;

temp = g_data;

Page 20: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

CAS?

− CAS가 없이는 대부분의 non-blocking

알고리즘들을 구현할 수 없다.

Queue, Stack, List…

− CAS를 사용하면 모든 싱글쓰레드 알고리즘

들을 Lock-free 알고리즘으로 변환할 수 있다!!!

− Lock-free 알고리즘의 핵심

2-

20

Page 21: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

정리

− Lock-free 알고리즘을 써야한다.

성능때문이다.

CAS가 꼴 필요하다.

CAS − CAS(&A, old, new);

− 의미 : A의 값이 old면 new로 바꾸고 true를 리턴

− 다른 버전의 의미 : A메모리를 다른 쓰레드가 먼저

업데이트 해서 false가 나왔다. 모든 것을 포기하라.

2-

21

Page 22: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Lock-free 알고리즘은 어떻게 구현되는가?

알고리즘의 동작이란?

− 기존의 자료구조의 구성을 다른 구성으로 변경하거나

자료구조에서 정보를 얻어내는 행위

2-

22

3 Head Tail

1 9 X

3 Head

Tail

1 9 X

push(35);

35

Page 23: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Lock-free 알고리즘은 어떻게 구현되는가?

2-

23

자료구조의 변경을 시도한다.

성공했는가? 완료 yes

no

(time machine) 시도 전으로 되돌아간다..

???

???

Page 24: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

Lock-free 알고리즘은 어떻게 구현되는가?

앞의 알고리즘이 불가능 하므로

2-

24

자료구조의 변경을 시도한다.

but, 다른 쓰레드가 먼저 변경했으면 시도 취소.

성공했는가? 완료 yes

no

현재의 자료구조를 파악한다.

Page 25: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

2-

25

자료구조의 변경을 시도한다.

but, 다른 쓰레드가 먼저 변경했으면 시도 취소.

CAS

while (true) {

int old_sum = sum;

if (true == CAS(&sum, old_sum, old_sum+2)) break;

}

결과물

EnterCriticalSection(&mylock);

sum = sum + 2;

LeaveCriticalSection(&mylock);

Page 26: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

2-

26

자료구조의 변경을 시도한다.

but, 다른 쓰레드가 먼저 변경했으면 시도 취소.

CAS LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL != next) continue;

if (CAS(&(last->next), NULL, e,

&tail, last, e)) return;

}

}

QUEUE::push(int x) {

Node *e = new Node(x);

tail->next = e;

tail = e;

}

Page 27: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

2-

27

현실

LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL != next) continue;

if (CAS(&(last->next), NULL, e,

&tail, last, e)) return;

}

} LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL == next) {

if (CAS(&(last->next), NULL, e)) {

CAS(&tail, last, e);

return;

}

} else CAS(&tail, last, next);

} }

하지만 2개의 변수에 동시에

CAS를 적용할 수 는 없다!

Page 28: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

− 알고리즘이 많이 복잡하다.

− 그래서 작성시 실수하기가 쉽다.

− 실수를 적발하기가 어렵다.

하루에 한두 번 서버 크래시

가끔 가다가 아이템 증발

− 제대로 동작하는 것이 증명된 알고리즘을

사용해야 한다.

2-

28

Page 29: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

대책

결론

− 믿을 수 있는 non-blocking container들을

사용하라.

Intel TBB, Visual Studio PPL

− 자신을 포함한 출처가 의심스러운 알고리즘은

정확성을 증명하고 사용하라.

정확성이 증명된 논문에 있는 알고리즘은 OK.

2-

29

Page 30: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

목차

도입

대책

성능 : 데스크탑에서 동접 1만 서버를

만들어 보자.

미래 또는 현실

2-

30

Page 31: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

성능

간단한 MMORPG 서버를 만들어 보자

− 1000x1000 world

− 60x60 sector

− 시야 30

− 1000마리의 몬스터

플레이어 접근 시 자동 이동 및 공격

− 이동/공격/채팅 가능

− Windows에서 IOCP로 구현

Page 32: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

성능

성능 향상을 위해

− 시야 처리시 검색 성능을 위해 월드를 sector로

분할하여 주위 sector만 검색

병렬 검색을 위해 tbb::concurrent_hash_map 사용

− 몬스터 AI 처리를 모든 쓰레드에서 균등하게 나누어

처리하기 위해 timer와 event시스템 사용

timer queue 병렬 등록을 위해

tbb::concurrent_priority_queue를 사용

− 객체 id의 재사용을 막고 메모리 재사용을 위해 객체

id와 객체 배열의 인덱스를 쌍으로 관리

<id, index>의 병렬 검색을 위해 tbb::concurrent_hash_map

사용

Page 33: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

성능

성능 측정용 DummyClient

− 서버에게 부하를 걸 수 있도록 여러 명의 Player를

에뮬레이션 해주는 프로그램

− 사람이 플레이 하는 것과 비슷하게 Player를

주기적으로 랜덤한 방향으로 이동 시킴

− 한명의 유저당 하나의 소켓 연결을 하므로 서버에서는

일반 클라이언트 접속과 DummyClient접속이 서로

차이가 없음

− 훌륭한 UNIT TESTER

− /* 이것도 IOCP로 구현, Direct3D로 유저 분포 실시간

디스플레이 */

Page 34: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

성능

2-

34

Page 35: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

성능

실행 결과

− Intel i7 920 2.67GHz 머신에서 실행

동접 8000 정도까지

− Lock-free 자료구조로 최적화 하기 전에는 동접

3000 정도가 한계.

Page 36: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

목차

도입

대책

성능

미래 또는 현실 : Transactionl 메모리,

그리고…

2-

36

Page 37: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

멀티쓰레드 프로그래밍 도우미 (1/2)

− Intel TBB

좋은 성능, 유용한 라이브러리

Concurrent 자료 구조

− Visual Studio PPL

Intel TBB와 유사

− OpenMP

컴파일러 레벨에서 병렬 프로그램 API를 제공

성능과 골치 아픈 문제들은 그대로

Page 38: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

멀티쓰레드 프로그래밍 도우미 (2/2)

− CUDA, OpenCL, DirectCompute

멀티코어 활용이 아니라 GPU활용

렌더링 하느라 바쁜 GPU를 건드리지 마세요.

I/O처리가 불가능해서 서버 Core Logic에는 적용

불가능

Page 39: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

암담한 현실

− Blocking Algorithm

성능 저하, priority inversion, convoying

Deadlock!

− Non-blocking Algorithm

높은 난이도로 인한 생산성 저하

어쩌라고????

Transactional Memory!

Page 40: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

Transactional Memory?

2-40

Tim Sweeny at GameTech2010

Page 41: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

Transactional Memory는

− 아까 본 바로 그 그림을 그대로 구현한 것…

자료구조의 변경을 시도한다.

성공했는가? 완료 yes

no

(time machine) 시도 전으로 되돌아간다..

Page 42: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

Transactional Memory가 좋은 이유?

− 생산성!

− 싱글쓰레드 프로그램을 그대로 쓸 수 있음

하지만 멀티쓰레드에서 Lock-free하게 실행됨.

LF_QUEUE::push(int x) {

atomic {

Node *e = new Node(x);

tail->next = e;

tail = e;

}

}

LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL == next) {

if (CAS(&(last->next), NULL, e)) {

CAS(&tail, last, e);

return;

}

} else CAS(&tail, last, next);

} }

Lock-free 구현 Transactionl Memory 구현

Page 43: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

Transactional Memory의 사용법

− 다른 쓰레드와 충돌이 일어날 수 있는 구간을

transaction으로 선언한다.

끝, 이게 전부

Page 44: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

어떻게 가능한가???

1. transaction 구간에서 읽고 쓴 메모리를 다른

쓰레드에서 접근했는지 검사한다.

write는 실제 메모리에 쓰지 않고 잠시 대기

2. 다른쓰레드에서의 접근이 있었으면 모든

업데이트를 무효화 한 후 다시 시도.

3. 다른쓰레드에서의 접근이 없었다면 transaction

구간에서의 update를 실제 메모리에 update

Page 45: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

그게 가능하다고??

어떻게

− Software적으로 메모리 업데이트를 관리하면

됨 (STM: Software Transactional Memory)

− 또는, 하드웨어가 알아서 해줌 (HTM:

Hardware Transactional Memory)

2-45

Page 46: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

Transactional Memory

장점

− 높은 생산성

기존의 프로그램을 그대로 사용 가능

단점

− STM : 오버헤드로 인한 속도 저하

− HTM : HW 필요.

한계점

− 쓰레드가 너무 많을 경우 잦은 충돌로 인한 성능향상의

한계

2-46

Page 47: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

Transactional Memory

지금까지는 꿈속의 개념이고 8 Core이상에서

STM의 성능을 기대하는 정도였으나.

Intel에서 일을 저지름.

2-47

2013년 6월 HASWELL말매

Haswell은 HTM을 지원.

Page 48: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

현실

Intel Haswell

2-48

Page 49: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

트랜잭션 메모리의 구현

Haswell의 HTM

− 복수개의 메모리에 대한 Transaction을

허용한다.

Cache Line 8개 까지

− CPU에서 transaction 실패시의 복구를

제공한다.

메모리와 레지스터의 변경을 모두 Roll-back한다.

− Visual Studio 2012, update 2에서 지원

2-49

Page 50: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

트랜잭션 메모리의 구현

하드웨어 트랜잭션 메모리 예제

2-50

DWORD WINAPI ThreadFunc(LPVOID lpVoid)

{

for (int i=0;i<500000000 / num_thread;++i) {

while (_xbegin() != _XBEGIN_STARTED) _xabort(0);

sum += 2;

_xend();

}

return 0;

}

Page 51: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

트랜잭션 메모리의 구현

하드웨어 트랜잭션 메모리 예제 (Set의 Add)

2-51

bool Add(int key)

{

NODE *pred, *curr;

NODE *node = new NODE(key);

while(true) {

pred = &head;

curr = pred->next;

while (curr->key < key) {

pred = curr;

curr = curr->next;

}

if (_XBEGIN_STARTED != my_xbegin()) { _xabort(0); continue; }

if (!validate(pred, curr)) {

_xabort(0);

continue;

}

if (key == curr->key) {

_xend();

delete node;

return false;

} else {

node->next = curr;

pred->next = node;

_xend();

return true;

}

}

}

HTM

Lock-Free

Page 52: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

트랜잭션 메모리의 구현

Haswell HTM의 한계

− 모든 알고리즘에 적용 불가능

HW 용량 한계 => 알고리즘의 맞춤형 수정 필요.

Nested Transaction불가능

− 오버헤드

모든 레지스터 내용 저장 및 Roll-back

2-52

그리고…

Page 53: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

트랜잭션 메모리의 구현 2-53

휴…

밥줄 끊길뻔…

Page 54: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

미래

HTM이 업그레이드 되어서 보급되면

끝인가?

− 쓰레드가 많아 질 수록 충돌확률이 올라가

TM의 성능이 떨어진다.

− 64Core 정도가 한계일 것이라고 예측하고

있다. (2010 GameTech, Tim Sweeny)

Page 55: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

미래

왜 쓰레드사이에 충돌이 생기는가?

− C 스타일 언어를 사용하기 때문이다.

공유 메모리

side effect

해결책은? C 비슷한 언어를 버린다.

− 대신 함수형 언어 사용

공유 메모리 없고 side effect없음

Page 56: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

새로운 언어

주목받고 있는 언어

− 하스켈

순수 함수형 언어로 1990년에 개발

개념은 뛰어나나 난이도로 인해 많이 사용되지 못하고 있음.

− Earlang

에릭슨에서 전자 계산기용으로 1982년에 개발

Scalable한 서버 시스템에 자주 사용되고 있음

2010GDC

Page 57: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

새로운 언어

함수형 언어의 문제

− 익히기 어렵다.

Page 58: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

정리

Lock-free 알고리즘이 무엇인가?

− 어떤 조건을 만족해야 하는가?

− 어떻게 구현되는가?

− 왜 어려운가?

− 하지만 왜 써야만 하는가.

멀티쓰레드 프로그래밍의 미래.

− Transactional Memory

진격의 INTEL

− 새로운 언어의 필요

2-58

Page 59: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

NEXT

다음 주제(내년???)

− Lock-free search : SKIP-LIST

− ABA Problem, aka 효율적인 reference

counting

− 고성능 MMO서버를 위한 non-

blocking자료구조의 활용

2-59

Page 60: 시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?

Q&A

연락처

[email protected]

− 발표자료 : ftp://210.93.61.41 id:ndc21 passwd: <바람의나라>

− Slideshare에 올릴 예정.

참고자료

− Herlihy, Shavit, “The Art of Multiprocesor Programming,

Revised”, Morgan Kaufman, 2012

2-60