Thread Synchronization 11/17/16
Threading: core ideas • Threads allow more efficient use of resources. • Multiple cores • Down time while waiting for I/O • Threads are better than processes for parallelism. • Cheaper to create and context switch • Easier to share information • Threading makes programming harder. • Need to think about how to split a problem up • Need to think about how threads interact
Create and Join • Each process starts with a single thread. • Any thread can spawn new threads with create . • Starts a new call stack for the thread. • create specifies what function the thread starts with. • Processes always start with main . • Different threads can start with different functions. • Returns the ID of the new thread. • join causes one thread to block until another thread completes. • join must specify the ID of the thread to wait for. • join gives access to the thread function’s return value.
Create and Join example IMPORTANT: this is not main(){ correct C code. We will double x = 1, y = -1; talk about the pthreads library next week. tid t1, t2; double res; t1 = create(worker, x); t2 = create(worker, y); res = join(t1); res += join(t2); printf("%d\n",res); } worker(double d){ do_work(&d); return d; }
Create and Join illustrated main thread create() peer thread 1 create() peer thread 2 join(t1) do_work(&d) do_work(&d) main thread waits for return d; thread 1 to terminate return d; (peer threads join(t1) returns terminate) join(t2) join(t2) returns printf() exit() terminates main thread and any peer threads
Thread Ordering (Why threads require care. Reasoning about this is hard.) • As a programmer you have no idea when threads will run. The OS schedules them, and the schedule will vary across runs. • It might decide to context switch from one thread to another at any time . • Your code must be prepared for this! • Ask yourself: “Would something bad happen if we context switched here?”
Example: The Credit/Debit Problem • Say you have $1000 in your bank account • You deposit $100 • You also withdraw $100 • How much should be in your account? • What if your deposit and withdrawal occur at the same time, at different ATMs?
Credit/Debit Problem: Race Condition Thread T 0 Thread T 1 Credit (int a) { Debit (int a) { int b; int b; b = ReadBalance (); b = ReadBalance (); b = b + a; b = b - a; WriteBalance (b); WriteBalance (b); PrintReceipt (b); PrintReceipt (b); } }
Credit/Debit Problem: Race Condition Say T 0 runs first Read $1000 into b Thread T 0 Thread T 1 Credit (int a) { Debit (int a) { int b; int b; b = ReadBalance (); b = ReadBalance (); b = b + a; b = b - a; WriteBalance (b); WriteBalance (b); PrintReceipt (b); PrintReceipt (b); } }
Credit/Debit Problem: Race Condition Say T 0 runs first Read $1000 into b Switch to T 1 Thread T 1 Thread T 0 Read $1000 into b Debit by $100 Debit (int a) { Credit (int a) { Write $900 int b; int b; b = ReadBalance (); b = ReadBalance (); b = b - a; b = b + a; WriteBalance (b); WriteBalance (b); PrintReceipt (b); PrintReceipt (b); } }
Credit/Debit Problem: Race Condition Race Condition: outcome Say T 0 runs first depends on scheduling order of concurrent threads. Read $1000 into b Switch to T 1 Thread T 1 Thread T 0 Read $1000 into b Debit by $100 Debit (int a) { Credit (int a) { Write $900 int b; int b; b = ReadBalance (); b = ReadBalance (); b = b - a; b = b + a; WriteBalance (b); WriteBalance (b); PrintReceipt (b); PrintReceipt (b); } } Switch back to T 0 Bank gave you $100! Read $1000 into b Credit $100 What went wrong? Write $1100
“Critical Section” Thread T 1 Thread T 0 Debit (int a) { Credit (int a) { int b; int b; Badness if context b = ReadBalance (); b = ReadBalance (); switch b = b - a; b = b + a; WriteBalance (b); here! WriteBalance (b); PrintReceipt (b); PrintReceipt (b); } } Bank gave you $100! What went wrong?
To Avoid Race Conditions Thread 0 Thread 1 ------------ ------------ ------------ ------------ - Critical - - Critical - - Section - - Section - ------------ ------------ ------------ ------------ 1. Identify critical sections 2. Use synchronization to enforce mutual exclusion • Only one thread active in a critical section
What Are Critical Sections? • Sections of code executed by multiple threads • Access shared variables, often making local copy • Places where order of execution or thread interleaving will affect the outcome • Must run atomically with respect to each other • Atomicity: runs as an entire unit or not at all. Cannot be divided into smaller parts.
Which code region is a critical section? Thread A Thread B thread_main() thread_main () { int a,b; { int a,b; a = getShared(); a = getShared(); A b = 20; b = 10; C D E a = a - b; a = a + b; saveShared(a); saveShared(a); B a += 1 a += 1 shared return a; return a; memory } } s = 40;
Which values might the shared s variable hold after both threads finish? Thread A Thread B thread_main () thread_main () { int a,b; { int a,b; a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); return a; return a; } } shared memory A. 30 s = 40; B. 20 or 30 C. 20, 30, or 50 D. Another set of values
If A runs first Thread A Thread B main () main () { int a,b; { int a,b; a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); return a; return a; } } shared memory s = 50;
B runs after A Completes Thread A Thread B main () main () { int a,b; { int a,b; a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); return a; return a; } } shared memory s = 30;
What about interleaving? Thread A Thread B main () main () { int a,b; { int a,b; a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); return a; return a; } } shared memory s = 40;
Is there a race condition? Suppose count is a global variable, multiple threads increment it: count++; A. Yes, there’s a race condition ( count++ is a critical section). B. No, there’s no race condition ( count++ is not a critical section). C. Cannot be determined. How about if compiler implements it as: movl (%edx), %eax // read count value addl $1, %eax // modify value movl %eax, (%edx) // write count How about if compiler implements it as: incl (%edx) // increment value
Mutex Locks The OS provides the following atomic operations: • Acquire/lock a mutex. • If no other thread has locked the mutex, claim it. • If another thread holds the mutex, block. • Threads unblocked in FIFO order. • Release/unlock a mutex. To enforce a critical section: • Before the critical section, lock the mutex. • After the critical section unlock the mutex.
Using Locks Thread A Thread B main () main () { int a,b; { int a,b; a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); shared return a; return a; memory } } s = 40;
Using Locks Thread A Thread B main () main () { int a,b; { int a,b; acquire(l); acquire(l); a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); release(l); release(l); shared return a; return a; memory } } s = 40; Held by: Nobody Lock l;
Using Locks Thread A Thread B main () main () { int a,b; { int a,b; acquire(l); acquire(l); a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); release(l); release(l); shared return a; return a; memory } } s = 40; Held by: Thread A Lock l;
Using Locks Thread A Thread B main () main () { int a,b; { int a,b; acquire(l); acquire(l); a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); release(l); release(l); shared return a; return a; memory } } s = 40; Held by: Thread A Lock l;
Using Locks Lock already owned. Must Wait! Thread A Thread B main () main () { int a,b; { int a,b; acquire(l); acquire(l); a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); release(l); release(l); shared return a; return a; memory } } s = 40; Held by: Thread A Lock l;
Using Locks Thread A Thread B main () main () { int a,b; { int a,b; acquire(l); acquire(l); a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); release(l); release(l); shared return a; return a; memory } } s = 50; Held by: Nobody Lock l;
Using Locks Thread A Thread B main () main () { int a,b; { int a,b; acquire(l); acquire(l); a = getShared(); a = getShared(); b = 20; b = 10; a = a - b; a = a + b; saveShared(a); saveShared(a); release(l); release(l); shared return a; return a; memory } } s = 30; Held by: Thread B Lock l;
Recommend
More recommend