1 / 21

CS 3214 Computer Systems

Learn about multi-threading and thread APIs in computer systems, including POSIX threads, Java, and C#. Understand the use of multi-threading through examples and explore concepts like race conditions.

msommers
Download Presentation

CS 3214 Computer Systems

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CS 3214Computer Systems Godmar Back Lecture 20

  2. Announcements • Project 4 due Apr 13 • Don’t procrastinate. • Exercise 9 due Apr 9 • Stay tuned for exercise 10 CS 3214 Spring 2010

  3. Some of the following slides are taken with permission from Complete Powerpoint Lecture Notes forComputer Systems: A Programmer's Perspective (CS:APP) Randal E. Bryant and David R. O'Hallaron http://csapp.cs.cmu.edu/public/lectures.html Multi-Threading Threads and Processes CS 3214 Spring 2010

  4. MULTI-THREADING CS 3214 Spring 2010

  5. Threading APIs • How are threads embedded in a language/environment? • POSIX Threads Standard (in C) • pthread_create(), pthread_join() • Uses function pointer • Largely retrofitted in Unix world • Needed to define interaction of signals and threads • Java/C# • Thread.start(), Thread.join() • Java: Using “Runnable” instance • C#: Uses “ThreadStart” delegate CS 3214 Spring 2010

  6. Example pthread_create/join static void * test_single(void *arg) { // this function is executed by each thread, in parallel } pthread_t threads[NTHREADS]; int i; for (i = 0; i < NTHREADS; i++) if (pthread_create(threads + i, (const pthread_attr_t*)NULL, test_single, (void*)i) == -1) { printf("error creating pthread\n"); exit(-1); } /* Wait for threads to finish. */ for (i = 0; i < NTHREADS; i++) pthread_join(threads[i], NULL); Use Default Attributes – could set stack addr/size here 2nd arg could receive exit status of thread CS 3214 Spring 2010

  7. Java Threads Example public class JavaThreads { public static void main(String []av) throws Exception { Thread [] t = new Thread[5]; for (int i = 0; i < t.length; i++) { final int tnum = i; Runnable runnable = new Runnable() { public void run() { System.out.println("Thread #"+tnum); } }; t[i] = new Thread(runnable); t[i].start(); } for (int i = 0; i < t.length; i++) t[i].join(); System.out.println("all done"); } } Use this form of explicit threading only when knowing number & rolesof threads before hand! Threads implements Runnable – could also have subclassed Thread & overridden run() Thread.join() can throw InterruptedException – can be used to interrupt thread waiting to join via Thread.interrupt CS 3214 Spring 2010

  8. import java.util.concurrent.*; public class FixedThreadPool { public static void main(String []av) throws Exception { ExecutorService ex = Executors.newFixedThreadPool(4); final int N = 4; Future<?> f[] = new Future<?>[N]; for (inti = 0; i < N; i++) { final int j = i; f[i] = ex.submit(new Callable<String>() { public String call() { return "Future #" + j + " brought to you by “ + Thread.currentThread(); } }); } System.out.println("Main thread: " + Thread.currentThread()); for (inti = 0; i < N; i++) System.out.println(f[i].get()); ex.shutdown(); } } Java Threadpools Tasks must implement “Callable” – like Runnable except returns result. get() waits for the execution of call() to be completed (if it hasn’t already) and returns result CS 3214 Spring 2010

  9. Explicit Threads vs. Pools • Overhead: • Startup overhead per thread relatively high (between 1e4 & 1e5 cycles); pools amortize • There is no point in having more threads than there are physical cores • Compete for available CPUs • Unless some subset is blocked on I/O or other conditions • Still, sizing of pools that maximizes throughput can be challenging • “cachedThreadPool” creates thread whenever needed, but reuses existing ones that are idle • “fixedThreadPool” - #of threads fixed • Can implement custom policies CS 3214 Spring 2010

  10. Accessing global variables /* Define a mutex and initialize it. */ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static int counter = 0; /* A global variable to protect. */ /* Function executed by each thread. */ static void * increment(void *_) { int i; for (i = 0; i < 1000000; i++) { pthread_mutex_lock(&lock); counter++; pthread_mutex_unlock(&lock); } } movl counter, %eax incl %eax movl %eax, counter CS 3214 Spring 2010

  11. Thread 1 movl counter, %eax incl %eax movl %eax, counter Thread 2 movl counter,%eax incl %eax movl %eax,counter Anatomy of a Race Condition IRQ  OS decides to context switch 0 time 0 IRQ  1 1 IRQ  1 %eax – Thread 1’s copy %eax – Thread 2’s copy counter – global variable, shared Assume counter == 0 initially 1 Final result: counter is 1, should be 2 CS 3214 Spring 2010

  12. Race Conditions • Definition: two or more threads read and write a shared variable, and final result depends on the order of the execution of those threads • Usually timing-dependent and intermittent • Hard to debug • Technically not a race condition if all execution orderings lead to same result • Chances are high that you misjudge this • How to deal with race conditions: • Ignore (!?) • Can be ok if final result does not need to be accurate • Not an option in CS 3214 • Don’t share: duplicate or partition state • Avoid “bad interleavings” that can lead to wrong result CS 3214 Spring 2010

  13. Not Sharing: Duplication or Partitioning • Undisputedly best way to avoid race conditions • Always consider it first • Usually faster than alternative of sharing + protecting • But duplicating has space cost; partitioning can have management cost • Sometimes must share (B depends on A’s result) • Examples: • Each thread has its own counter (then sum counters up after join()) • Every CPU has its own ready queue • Each thread has its own memory region from which to allocate objects • Truly ingenious solutions to concurrency involve a way to partition things people originally thought you couldn’t CS 3214 Spring 2010

  14. Thread-Local Storage • A concept that helps to avoid race conditions by giving each thread a copy of a certain piece of state • Recall: • All local variables are already thread-local • But their extent is only one function invocation! • All function arguments are also thread-local • But must pass them along call-chain • TLS creates variables of which there’s a separate location for each thread to store their value in. • In PThreads/C (compiler or library-supported) • Dynamic: pthread_create_key(), pthread_get_key(), pthread_set_key() • E.g. myvalue = keytable(key_a)get(pthread_self()); • Static: using __thread storage class • E.g.: __thread int x; • Java: java.lang.ThreadLocal CS 3214 Spring 2010

  15. TLS Example int main() { int i, N = 4; pthread_t t[N]; for (i = 0; i < N; i++) pthread_create(t + i, NULL, thread_func, (void *)i); for (i = 0; i < N; i++) pthread_join(t[i], NULL); return 0; } #include <pthread.h> #include <stdio.h> __thread int x; static void * thread_func(void * _tn) { int i, tn = (int)_tn; for (i = 0; i < 3; i++) printf("thread=%d x=%d\n", tn, x++); return NULL; } Compiler assigns offset in “per-thread” area; all accesses use this offset Note that scope of ‘x’ is global. CS 3214 Spring 2010

  16. Atomic Operations • All architectures support at least 1 atomic operation - so that no bad interleavings can occur because • a) CPU will not accept interrupts – so no context switch on same CPU • b) multiple cores synchronize by locking caches/buses • Variations include “test-and-set”, “compare-and-swap”, “compare-and-set”, etc. • Other atomic ops can be built on top of that • Example: java.util.concurrent.AtomicInteger • incrementAndGet() CS 3214 Spring 2010

  17. java.util.concurrent.AtomicInteger public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } public final int get() { return value; } native method that provides access to atomic instruction memory location encoded as (this, offset) CS 3214 Spring 2010

  18. Motivation for Critical Section • Partitioning or the use of atomic operations is not always possible • Must then prevent race conditions by imposing constraints on execution order so the final result is the same regardless of actual execution order • That is, exclude “bad” interleavings • Specifically: disallow other threads to start updating shared variables while one thread is in the middle of doing so; make those updates as a group atomic with respect to what other threads “see” – threads either see old or new value, but none in between CS 3214 Spring 2010

  19. Critical Sections • Critical Section • A synchronization technique to ensure atomic execution of a segment of code • Has entry() and exit() operations • Critical Section Problem also known as mutual exclusion problem pthread_mutex_lock(&lock); /* entry() */ counter++; pthread_mutex_unlock(&lock); /* exit() */ CS 3214 Spring 2010

  20. Critical Sections (cont’d) • Only one thread can be inside critical section; others attempting to enter CS must wait until thread that’s inside CS leaves it • Different solutions known: entirely software, or entirely hardware possible • Usually combined • Different implementations (but same API) for uniprocessor vs multiprocessor scenarios CS 3214 Spring 2010

  21. Myths about CS • A thread in a CS executes entire section without being preempted • No – not usually true • A thread in a CS executes the entire section • No – may exit or crash • There can be only one critical section in a program • No – as many as the programmer decides to use • Critical sections protect blocks of code • No – they protect data accesses (but note role of encapsulation!) CS 3214 Spring 2010

More Related