60
Threads

Threads - Hanyangcalab.hanyang.ac.kr/courses/SP_taesoo/11_threads.pdf · 2016-10-18 · Threads Multiple threads can be associated with a process Each thread has its own logical control

  • Upload
    others

  • View
    47

  • Download
    0

Embed Size (px)

Citation preview

Threads

Carnegie Mellon

2Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Traditional View of a Process

Process = process context + code, data, and stack

Shared libraries

Run-time heap

0

Read/write data

Program context: Data registers Condition codes Stack pointer (SP) Program counter (PC)

Code, data, and stack

Read-only code/data

StackSP

PC

brk

Process context

Kernel context: VM structures Descriptor table brk pointer

Carnegie Mellon

3Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Alternate View of a Process

Process = thread + code, data, and kernel context

Shared libraries

Run-time heap

0

Read/write dataThread context: Data registers Condition codes Stack pointer (SP) Program counter (PC)

Code, data, and kernel context

Read-only code/data

StackSP

PC

brk

Thread (main thread)

Kernel context: VM structures Descriptor table brk pointer

Carnegie Mellon

4Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

A Process With Multiple Threads

Multiple threads can be associated with a process Each thread has its own logical control flow Each thread shares the same code, data, and kernel context Each thread has its own stack for local variables

but not protected from other threads Each thread has its own thread id (TID)

Thread 1 context: Data registers Condition codes SP1 PC1

stack 1

Thread 1 (main thread)

shared libraries

run-time heap

0

read/write data

Shared code and data

read-only code/data

Kernel context: VM structures Descriptor table brk pointer

Thread 2 context: Data registers Condition codes SP2 PC2

stack 2

Thread 2 (peer thread)

Carnegie Mellon

5Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Logical View of Threads

Threads associated with process form a pool of peers Unlike processes which form a tree hierarchy

P0

P1

sh sh sh

foo

bar

T1

Process hierarchyThreads associated with process foo

T2T4

T5 T3

shared code, dataand kernel context

Carnegie Mellon

6Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Concurrent Threads

Two threads are concurrent if their flows overlap in time

Otherwise, they are sequential

Examples: Concurrent: A & B, A&C Sequential: B & C

Time

Thread A Thread B Thread C

Carnegie Mellon

7Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Concurrent Thread Execution

Single Core Processor Simulate parallelism

by time slicing

Multi-Core Processor Can have true

parallelism

Time

Thread A Thread B Thread C Thread A Thread B Thread C

Run 3 threads on 2 cores

Carnegie Mellon

8Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Threads vs. Processes How threads and processes are similar

Each has its own logical control flow Each can run concurrently with others (possibly on

different cores) Each is context switched

How threads and processes are different Threads share all code and data (except local stacks)

Processes (typically) do not Threads are somewhat less expensive than processes

Process control (creating and reaping) twice as expensive as thread control

Linux numbers:– ~20K cycles to create and reap a process– ~10K cycles (or less) to create and reap a thread

9

멀티 쓰레드 (multithread): 단일쓰레드의 이용 예

“a.txt” “파일을 b.txt” 파일로 복사한다 .

단일 쓰레드 구현

while( NOT eof){

read a line from a.txt into buffer B ;

write a line to b.txt from buffer B ;

}

cp a.txt b.txt

10

멀티 쓰레드 (multithread): 멀티쓰레드의 이용 예

“a.txt” “파일을 b.txt” 파일로 복사한다 .

다중 쓰레드 구현

cp a.txt b.txt

while( NOT eof){ read a line from a.txt into B ; wait till B is empty ;}

while( NOT eof){ wait till buffer B is full ; write a line from B to b.txt ;}

쓰레드 1 쓰레드 2

11

멀티 쓰레드 (multithread): 멀티 쓰레드를 이용한 파일 복사

쓰레드 1 쓰레드 2Buffer B

12

쓰레드 (thread)

쓰레드 (thread) 는 CPU 이용 (utilization) 의 기본 단위 .

쓰레드는 Light Weight Process 라고 부르기도 함 .

– 프로세스는 자신의 프로그램 카운터 , 레지스터 값들 , 주소공간을 가짐 .

– 쓰레드는 자신의 프로그램 카운터 , 레지스터 값들을 가짐 . 자신의 주소공간은“ ”없음

– 다른 쓰레드와 주소공간을 공유함 .

code

data

stack

프로세스주소공간(virtual address space)

Code:int a=3;a++;for(int i=0; i<10; i++) a++;

int a; // automaticregister int a; // registervolatile int a; // memory (cache or ram)

13

멀티 쓰레드 (multithread)

프로세스의 구성– 단일 쓰레드 프로세스– 멀티 쓰레드 프로세스

code data files

registers stack

code data files

regis-ters

regis-ters

regis-ters

stack stack stack

ThreadThread

shared

Carnegie Mellon

14Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Posix Threads (Pthreads) Interface Pthreads: Standard interface for ~60

functions that manipulate threads from C programs Creating and reaping threads

pthread_create() pthread_join()

Determining your thread ID pthread_self()

Terminating threads pthread_cancel() pthread_exit() exit() [terminates all threads] , RET [terminates

current thread] Synchronizing access to shared variables

pthread_mutex_init pthread_mutex_[un]lock

Carnegie Mellon

15Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

void *thread(void *vargp) /* thread routine */{ printf("Hello, world!\n"); return NULL; }

The Pthreads "hello, world" Program

/* * hello.c - Pthreads "hello, world" program */#include "csapp.h"void *thread(void *vargp);

int main(){ pthread_t tid; Pthread_create(&tid, NULL, thread, NULL); Pthread_join(tid, NULL); exit(0); }

Thread attributes (usually NULL)

Thread arguments(void *p)

Return value(void **p)

hello.c

Thread ID

Thread routine

hello.c

Carnegie Mellon

16Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Execution of Threaded “hello, world”

Main thread

Peer thread

return NULL;Main thread waits for peer thread to terminate

exit() Terminates

main thread and any peer threads

call Pthread_create()

call Pthread_join()

Pthread_join() returns

printf()

Peer threadterminates

Pthread_create() returns

17

Thread Creation

#include <pthread.h>

int pthread_create(pthread_t *restrict tidp,

const pthread_attr_t *restrict attr,

void *(*start_rtn)(void), void *restrict arg);

Returns: 0 if OK, error number on failure

Tidp: pointer to thread structure

Start_rtn: pointer to function to execute

argument

18

Thread Identification

#include <pthread.h>int pthread_equal(pthread_t tid1, pthread_t tid2);

Returns: nonzero if equal, 0 otherwise

#include <pthread.h>pthread_t pthread_self(void);

Returns: the thread ID of the calling thread

19

Thread Identification

pthread_t

– Linux 2.4.22: unsigned long integer

– Solaris 9: unsigned integer.

– FreeBSD 5.2.1 and Mac OS X 10.3: a pointer to the pthread structure for the pthread_t data, no portable way to print its value.

20

Printing thread IDs

#include "apue.h"#include <pthread.h>pthread_t ntid;

void printids(const char *s){pid_t pid;pthread_t tid;pid = getpid();tid = pthread_self();printf("%s pid %u tid %u (0x%x)\n", s,

(unsigned int)pid, (unsigned int)tid, (unsigned int)tid);}void * thr_fn(void *arg){

printids("new thread: ");return((void *)0);

}int main(void){

int err;err = pthread_create(&ntid, NULL, thr_fn, NULL);if (err != 0)

err_quit("can't create thread: %s\n", strerror(err));printids("main thread:");sleep(1);exit(0);

}

21

Printing thread IDs

Solaris gives us

$ ./a.outmain thread: pid 7225 tid 1 (0x1)new thread: pid 7225 tid 4 (0x4)

FreeBSD gives us

Mac OS X to be similar to FreeBSD; however, address ranges are different

$ ./a.outmain thread: pid 14954 tid 134529024 (0x804c000)new thread: pid 14954 tid 134530048 (0x804c400)

$ ./a.outmain thread: pid 779 tid 2684396012 (0xa000a1ec)new thread: pid 779 tid 25166336 (0x1800200)

22

Printing thread IDs

Linux

$ ./a.out

new thread: pid 6628 tid 1026 (0x402)

main thread: pid 6626 tid 1024 (0x400)

23

Thread Termination

A single thread can exit in three ways, thereby stopping its flow of control, with-

out terminating the entire process.

1. The thread can simply return from the start routine. The return value is the thread's exit code.

2. The thread can be canceled by another thread in the same process.

3. The thread can call pthread_exit.

#include <pthread.h>

void pthread_exit(void *rval_ptr);

24

Thread Termination

The calling thread will block until the specified thread calls pthread_exit, returns

from its start routine, or is canceled.

If the thread simply returned from its start routine, rval_ptr will contain the re-

turn code. If the thread was canceled, the memory location specified by rval_ptr

is set to PTHREAD_CANCELED.

#include <pthread.h>

int pthread_join(pthread_t thread, void **rval_ptr);

Returns: 0 if OK, error number on failure

25

Fetching the thread exit status#include "apue.h"#include <pthread.h>void * thr_fn1(void *arg){

printf("thread 1 returning\n");return((void *)1);

}void * thr_fn2(void *arg){

printf("thread 2 exiting\n");pthread_exit((void *)2);

}int main(void){

int err;pthread_t tid1, tid2;void *tret;err = pthread_create(&tid1, NULL, thr_fn1, NULL);if (err != 0)

err_quit("can't create thread 1: %s\n", strerror(err));err = pthread_create(&tid2, NULL, thr_fn2, NULL);if (err != 0)

err_quit("can't create thread 2: %s\n", strerror(err));err = pthread_join(tid1, &tret);if (err != 0)

err_quit("can't join with thread 1: %s\n", strerror(err));printf("thread 1 exit code %d\n", (int)tret);err = pthread_join(tid2, &tret);if (err != 0)

err_quit("can't join with thread 2: %s\n", strerror(err));printf("thread 2 exit code %d\n", (int)tret);exit(0);

}

26

Fetching the thread exit status

Running the program in Figure 11.3 gives us

$ ./a.outthread 1 returningthread 2 exitingthread 1 exit code 1thread 2 exit code 2

27

Incorrect use of pthread_exit argument

#include "apue.h"#include <pthread.h>

struct foo { int a, b, c, d;};

void printfoo(const char *s, const struct foo *fp){ printf(s); printf(" structure at 0x%x\n“, (unsigned)fp); printf(" foo.a = %d\n", fp->a); printf(" foo.b = %d\n", fp->b); printf(" foo.c = %d\n", fp->c); printf(" foo.d = %d\n", fp->d);}

void * thr_fn1(void *arg){ struct foo foo = {1, 2, 3, 4}; printfoo("thread 1:\n", &foo); pthread_exit((void *)&foo);}

void * thr_fn2(void *arg){ printf("thread 2: ID is %d\n“, pthread_self()); pthread_exit((void *)0);}

28

Incorrect use of pthread_exit argument

int main(void){

int err;

pthread_t tid1, tid2;

struct foo *fp;

err = pthread_create(&tid1, NULL, thr_fn1, NULL);

if (err != 0)

err_quit("can't create thread 1: %s\n", strerror(err));

err = pthread_join(tid1, (void *)&fp);

if (err != 0)

err_quit("can't join with thread 1: %s\n", strerror(err));

sleep(1);

printf("parent starting second thread\n");

err = pthread_create(&tid2, NULL, thr_fn2, NULL);

if (err != 0)

err_quit("can't create thread 2: %s\n", strerror(err));

sleep(1);

printfoo("parent:\n", fp);

exit(0);

}

29

Incorrect use of pthread_exit argument

the problem with using an automatic variable as the argument to

pthread_exit.

When we run this program on Linux, we get

$ ./a.outthread 1:structure at 0x409a2abcfoo.a = 1foo.b = 2foo.c = 3foo.d = 4parent starting second threadthread 2: ID is 32770parent:structure at 0x409a2abcfoo.a = 0foo.b = 32770foo.c = 1075430560foo.d = 1073937284

30

Example – Figure 11.4 Incorrect use of pthread_exit argument

The results on FreeBSD are similar:

$ ./a.outthread 1:structure at 0xbfafefc0foo.a = 1foo.b = 2foo.c = 3foo.d = 4parent starting second threadthread 2: ID is 134534144parent:structure at 0xbfafefc0foo.a = 0foo.b = 134534144foo.c = 3foo.d = 671642590

31

Thread Termination

One thread can request that another in the same process be canceled by call-

ing the pthread_cancel function.

pthread_cancel will cause the thread specified by tid to behave as if it had

called pthread_exit with an argument of PTHREAD_CANCELED.

Note that pthread_cancel doesn‘t wait for the thread to terminate. It merely

makes the request.

#include <pthread.h>

int pthread_cancel(pthread_t tid);

Returns: 0 if OK, error number on failure

32

Thread Termination

A thread can arrange for functions to be called when it exits.: thread cleanup

handlers.

More than one cleanup handler can be established for a thread. The handlers

are recorded in a stack, which means that they are executed in the reverse or-

der from that with which they were registered.

#include <pthread.h>

void pthread_cleanup_push(void (*rtn)(void *), void *arg);

void pthread_cleanup_pop(int execute);

33

Thread Termination Cleanup handlers are called when

– Makes a call to pthread_exit

– Responds to a cancellation request

– Makes a call to pthread_cleanup_pop with a nonzero execute argument

– If the execute argument is set to zero, the cleanup function is not called. In either

case, pthread_cleanup_pop removes the cleanup handler established by the last call

to pthread_cleanup_push.

34

Example – Figure 11.5 Thread cleanup handler#include "apue.h"#include <pthread.h>void cleanup(void *arg){ printf("cleanup: %s\n", (char *)arg);}

void * thr_fn1(void *arg){ printf("thread 1 start\n"); pthread_cleanup_push(cleanup, "thread 1 first handler"); pthread_cleanup_push(cleanup, "thread 1 second handler"); printf("thread 1 push complete\n"); if (arg) return((void *)1); pthread_cleanup_pop(0); pthread_cleanup_pop(0); return((void *)1);}

void * thr_fn2(void *arg){ printf("thread 2 start\n"); pthread_cleanup_push(cleanup, "thread 2 first handler"); pthread_cleanup_push(cleanup, "thread 2 second handler"); printf("thread 2 push complete\n"); if (arg) pthread_exit((void *)2); pthread_cleanup_pop(0); pthread_cleanup_pop(0); pthread_exit((void *)2);}

35

Example – Figure 11.5 Thread cleanup handler

int main(void){ int err; pthread_t tid1, tid2; void *tret; err = pthread_create(&tid1, NULL, thr_fn1, (void *)1); if (err != 0) err_quit("can't create thread 1: %s\n", strerror(err)); err = pthread_create(&tid2, NULL, thr_fn2, (void *)1); if (err != 0) err_quit("can't create thread 2: %s\n", strerror(err)); err = pthread_join(tid1, &tret); if (err != 0) err_quit("can't join with thread 1: %s\n", strerror(err)); printf("thread 1 exit code %d\n“, (int)tret); err = pthread_join(tid2, &tret); if (err != 0) err_quit("can't join with thread 2: %s\n", strerror(err)); printf("thread 2 exit code %d\n“, (int)tret); exit(0);}

36

Thread cleanup handler

Note that although we never intend to pass a nonzero argument to the

thread start-up routines, we still need to match calls to

pthread_cleanup_pop with the calls to pthread_cleanup_push; other-

wise, the program might not compile.

Running the program in Figure 11.5 gives us

$ ./a.outthread 1 startthread 1 push completethread 2 startthread 2 push completecleanup: thread 2 second handlercleanup: thread 2 first handlerthread 1 exit code 1thread 2 exit code 2

37

Thread Termination

Process

Thread Description

fork pthread_create create a new flow of con-trol

exit pthread_exit exit from an existing flow of control

wait-pid

pthread_join get exit status from flow of control

atexit pthread_cleanup_push

register function to be called at exit from flow of control

get-pid

pthread_self get ID for flow of control

abort pthread_cancel request abnormal termi-nation of flow of control

Carnegie Mellon

38Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Pros and Cons of Thread-Based Designs

+ Easy to share data structures between threads e.g., logging information, file cache

+ Threads are more efficient than processes

– Unintentional sharing can introduce subtle and hard-to-reproduce errors! The ease with which data can be shared is both the

greatest strength and the greatest weakness of threads Hard to know which data shared & which private Hard to detect by testing

Probability of bad race outcome very low But nonzero!

Carnegie Mellon

39Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Shared Variables in Threaded C Programs

Question: Which variables in a threaded C program are shared? The answer is not as simple as “global variables are shared”

and “stack variables are private”

Def: A variable x is shared if and only if multiple threads reference some instance of x.

Requires answers to the following questions: What is the memory model for threads? How are instances of variables mapped to memory? How many threads might reference each of these instances?

Carnegie Mellon

40Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Threads Memory Model Conceptual model:

Multiple threads run within the context of a single process Each thread has its own separate thread context

Thread ID, stack, stack pointer, PC, condition codes, and GP registers

All threads share the remaining process context Code, data, heap, and shared library segments of the process virtual

address space Open files and installed handlers

Operationally, this model is not strictly enforced: Register values are truly separate and protected, but… Any thread can read and write the stack of any other thread

The mismatch between the conceptual and operation model is a source of confusion and errors

Carnegie Mellon

41Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Example Program to Illustrate Sharing

char **ptr; /* global var */

int main(){ long i; pthread_t tid; char *msgs[2] = { "Hello from foo", "Hello from bar" };

ptr = msgs; for (i = 0; i < 2; i++) Pthread_create(&tid, NULL, thread, (void *)i); Pthread_exit(NULL);}

void *thread(void *vargp){ long myid = (long)vargp; static int cnt = 0;

printf("[%ld]: %s (cnt=%d)\n", myid, ptr[myid], ++cnt); return NULL;}

Peer threads reference main thread’s stackindirectly through global ptr variable

sharing.c

Carnegie Mellon

42Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Mapping Variable Instances to Memory

Global variables Def: Variable declared outside of a function Virtual memory contains exactly one instance of any

global variable

Local variables Def: Variable declared inside function without static attribute Each thread stack contains one instance of each local

variable

Local static variables Def: Variable declared inside function with the static attribute Virtual memory contains exactly one instance of any local

static variable.

Carnegie Mellon

43Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

char **ptr; /* global var */

int main(){ long i; pthread_t tid; char *msgs[2] = { "Hello from foo", "Hello from bar" };

ptr = msgs; for (i = 0; i < 2; i++) Pthread_create(&tid, NULL, thread, (void *)i); Pthread_exit(NULL);}

void *thread(void *vargp){ long myid = (long)vargp; static int cnt = 0;

printf("[%ld]: %s (cnt=%d)\n", myid, ptr[myid], ++cnt); return NULL;}

Mapping Variable Instances to Memory

Global var: 1 instance (ptr [data])

Local static var: 1 instance (cnt [data])

Local vars: 1 instance (i.m, msgs.m)

Local var: 2 instances ( myid.p0 [peer thread 0’s stack], myid.p1 [peer thread 1’s stack])

sharing.c

Carnegie Mellon

44Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Shared Variable Analysis Which variables are shared?

Answer: A variable x is shared iff multiple threads reference at least one instance of x. Thus:

ptr, cnt, and msgs are shared i and myid are not shared

Variable Referenced by Referenced by Referenced byinstance main thread? peer thread 0? peer thread 1?

ptrcnti.mmsgs.mmyid.p0myid.p1

yes yes yesno yes yesyes no noyes yes yesno yes nono no yes

Carnegie Mellon

45Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Synchronizing Threads Shared variables are handy...

…but introduce the possibility of nasty synchronization errors.

Carnegie Mellon

46Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

badcnt.c: Improper Synchronization

/* Global shared variable */volatile long cnt = 0; /* Counter */

int main(int argc, char **argv){ long niters; pthread_t tid1, tid2;

niters = atoi(argv[1]); Pthread_create(&tid1, NULL, thread, &niters); Pthread_create(&tid2, NULL, thread, &niters); Pthread_join(tid1, NULL); Pthread_join(tid2, NULL);

/* Check result */ if (cnt != (2 * niters)) printf("BOOM! cnt=%ld\n", cnt); else printf("OK cnt=%ld\n", cnt); exit(0);}

/* Thread routine */ void *thread(void *vargp) { long i, niters = *((long *)vargp); for (i = 0; i < niters; i++) cnt++; return NULL; }

linux> ./badcnt 10000OK cnt=20000linux> ./badcnt 10000BOOM! cnt=13051linux>

cnt should equal 20,000.

What went wrong?badcnt.c

Carnegie Mellon

47Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Assembly Code for Counter Loop

for (i = 0; i < niters; i++) cnt++;

C code for counter loop in thread i

movq (%rdi), %rcx testq %rcx,%rcx jle .L2 movl $0, %eax.L3: movq cnt(%rip),%rdx addq $1, %rdx movq %rdx, cnt(%rip) addq $1, %rax cmpq %rcx, %rax jne .L3.L2:

Hi : Head

Ti : Tail

Li : Load cntUi : Update cntSi : Store cnt

Asm code for thread i

Carnegie Mellon

48Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Concurrent Execution Key idea: In general, any sequentially

consistent interleaving is possible, but some give an unexpected result! Ii denotes that thread i executes instruction I %rdxi is the content of %rdx in thread i’s context

H1

L1

U1

S1

H2

L2

U2

S2

T2

T1

1111222221

-011-----1

0001111222

i (thread)instri cnt%rdx1

OK

-----1222-

%rdx2

Thread 1 critical sectionThread 2 critical section

Carnegie Mellon

49Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition

Concurrent Execution (cont) Incorrect ordering: two threads increment the

counter, but the result is 1 instead of 2

H1

L1

U1

H2

L2

S1

T1

U2

S2

T2

1112211222

-01--11---

0000011111

i (thread)instri cnt%rdx1

----0--111

%rdx2

Oops!

병행성 문제 병행성 (concurrency) 문제

두 개 이상의 스레드가 어떤 객체에 있는 하나의 데이터에 접근하게 되는 경우

서로 다른 두 스택에서 실행되는 메소드가 객체에 있는 동일한 객체에 대한 게터 또는 세터 메소드를 호출하게 되는 경우

스레드는 자신이 잠시 중단된 적이 있다는 것을 기억할 수가 없음

라이언과 모니카 이야기 절대 마이너스 통장이 되지 않도록 하자는 약속을 했음

라이언이 50 달러를 빼기 위해 은행 잔고 확인 , 100 달러가

남아있음을 확인했는데 잠이 들었음

라이언이 잠든 상태에서 모니카가 100 달러가 필요해서 잔

고를 확인하고 바로 돈을 인출

잠에서 깬 라이언은 통장 잔고가 여전히 100 달러라고 생각

하고 50 달러 인출

결국 통장 잔고가 마이너스가 됨

락 (lock)

상황에 따라 한 스레드가 사용중인 자원을 특정 작업이 끝날 때까지는 다른 스레드에서 쓸 수 없게 만드는 메커니즘

자물쇠 (lock) 열쇠

synchronized 키워드 (Java 언어 )

어떤 메소드를 한 번에 한 스레드만 접근할 수 있도록 만들 때는 synchronized 라는 키워드를 씀

synchronized 키워드는 그 동기화된 코드에 접근하려면 스레드가 열쇠를 가지고 있어야 한다는 것을 의미함

private synchronized void makeWithdrawal(int amount) {……}

54

Thread Synchronization Thread A reads the variable and then writes a new value to it, but the write op-

eration takes two memory cycles. If thread B reads the same variable between

the two write cycles, it will see an inconsistent value.

read

write 1

write 2

read

Thread A Thread B

time

read

read

read

write 1

write 2

Thread A

Thread B

55

Thread Synchronization - Mutexes

A mutex is basically a lock that we set (lock) before accessing a shared re-

source and release (unlock) when we're done. While it is set, any other thread

that tries to set it will block until we release it.

If more than one thread is blocked when we unlock the mutex, then all threads

blocked on the lock will be made runnable, and the first one to run will be able

to set the lock.

The others will see that the mutex is still locked and go back to waiting for it to

become available again. In this way, only one thread will proceed at a time.

56

Thread Synchronization - Mutexes

A mutex variable is represented by the pthread_mutex_t data type.

Before we can use a mutex variable, we must first initialize it by either setting it to

the constant PTHREAD_MUTEX_INITIALIZER (for statically-allocated mutexes

only) or calling pthread_mutex_init.

If we allocate the mutex dynamically (by calling malloc, for example), then we need

to call pthread_mutex_destroy before freeing the memory.

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex,

const pthread_mutexattr_t *restrict attr);

int pthread_mutex_destroy(pthread_mutex_t *mutex);

Both return: 0 if OK, error number on failure

57

Thread Synchronization - Mutexes

To lock a mutex, we call pthread_mutex_lock. If the mutex is already locked,

the calling thread will block until the mutex is unlocked. To unlock a mutex, we

call pthread_mutex_unlock.

Trylock: If the mutex is unlocked at the time pthread_mutex_trylock is called,

then pthread_mutex_trylock will lock the mutex without blocking and return 0.

Otherwise, pthread_mutex_trylock will fail, returning EBUSY without locking

the mutex.

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

All return: 0 if OK, error number on failure

58

Using a mutex to protect a data structure

#include <stdlib.h>#include <pthread.h>struct foo { int f_count; pthread_mutex_t f_lock; /* ... more stuff here ... */};struct foo * foo_alloc(void) /* allocate the object */{ struct foo *fp; if ((fp = malloc(sizeof(struct foo))) != NULL) { fp->f_count = 1; if (pthread_mutex_init(&fp->f_lock, NULL) != 0) { free(fp); return(NULL); } /* ... continue initialization ... */ } return(fp);}

59

Using a mutex to protect a data structure

void foo_hold(struct foo *fp) /* add a reference to the object */{ pthread_mutex_lock(&fp->f_lock); fp->f_count++; pthread_mutex_unlock(&fp->f_lock);}

void foo_rele(struct foo *fp) /* release a reference to the object */{ pthread_mutex_lock(&fp->f_lock); if (--fp->f_count == 0) { /* last reference */ pthread_mutex_unlock(&fp->f_lock); pthread_mutex_destroy(&fp->f_lock); free(fp); } else { pthread_mutex_unlock(&fp->f_lock); }}

60

Using a mutex to protect a data structure

When more than one thread needs to access a dynamically-allocated object, we

can embed a reference count in the object to ensure that we don't free its memory

before all threads are done using it.

We lock the mutex before incrementing the reference count, decrementing the ref-

erence count, and checking whether the reference count reaches zero.

Before using the object, threads are expected to add a reference count to it. When

they are done, they must release the reference. When the last reference is re-

leased, the object's memory is freed.