Programming with Threads Threads Sometimes called a lightweight

  • Slides: 24
Download presentation
Programming with Threads

Programming with Threads

Threads § Sometimes called a lightweight process § smaller execution unit than a process

Threads § Sometimes called a lightweight process § smaller execution unit than a process § Consists of: program counter register set stack space § Threads share: memory space code section OS resources(open files, signals, etc. )

Threads § A process is defined to have at least one thread of execution

Threads § A process is defined to have at least one thread of execution § A process may launch other threads which execute concurrently with the process § Switching between threads is faster No memory management issues, etc. § Mutual exclusion problems:

Threads Process 0 regs mem Process 1 code regs mem code All threads in

Threads Process 0 regs mem Process 1 code regs mem code All threads in a process share the same memory space

Single Threaded and Multithreaded Process Models Multithreaded Process Model Single-Threaded Process Model Process Control

Single Threaded and Multithreaded Process Models Multithreaded Process Model Single-Threaded Process Model Process Control Block User Address Space Thread Control Block Thread Control Block Process Control Block User Stack User Address Space Kernel Stack User Stack Kernel Stack Thread

Why Threads? § § § Software Portability Latency Hiding Scheduling and Load Balancing Speed

Why Threads? § § § Software Portability Latency Hiding Scheduling and Load Balancing Speed Ease of Programming and Widespread use § Program Maintenance

Thread issues § How should threads be scheduled compared to processes? Equal to processes

Thread issues § How should threads be scheduled compared to processes? Equal to processes Within the parent processes quantum § How are threads implemented kernel support (system calls) user level threads § What are the issues?

User-Level Threads § All thread management is done by the application § The kernel

User-Level Threads § All thread management is done by the application § The kernel is not aware of the existence of threads § Thread switching does not require kernel mode privileges § Scheduling is application specific

Kernel-Level Threads § Windows NT and Linux are examples of this approach § Kernel

Kernel-Level Threads § Windows NT and Linux are examples of this approach § Kernel maintains context information for the process and the threads § Switching between threads requires the kernel

POSIX Threads § Thread API available on many OS’s #include <pthread. h> cc myprog.

POSIX Threads § Thread API available on many OS’s #include <pthread. h> cc myprog. c –o myprog -lpthread § Thread creation int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg); § Thread termination void pthread_exit(void *retval); § Waiting for Threads int pthread_join(pthread_t th, void **thread_return);

#include <pthread. h> #include <stdio. h> int print_message_function( void *ptr ); int x =

#include <pthread. h> #include <stdio. h> int print_message_function( void *ptr ); int x = 1; main() { pthread_t thread 1, thread 2; int thread 1 result, thread 2 result; char *message 1 = "Hello"; char *message 2 = "World"; pthread_attr_t *pthread_attr_default = NULL; int print_message_function( void *ptr ) { char *message; message = (char *) ptr; printf("%s ", message); fflush(stdout); return x++; } printf("Beginn"); pthread_create( &thread 1, pthread_attr_default, (void*)&print_message_function, (void*) message 1); pthread_create(&thread 2, pthread_attr_default, (void*)&print_message_function, (void*) message 2); pthread_join(thread 1, (void *)&thread 1 result); printf("End thread 1 with %dn", thread 1 result); pthread_join(thread 2, (void *)&thread 2 result); printf("End thread 2 with %dn", thread 2 result); exit(0); }

#include <sys/types. h> #include <sys/socket. h> server() { SOCKET listen. Skt, new. Skt; struct

#include <sys/types. h> #include <sys/socket. h> server() { SOCKET listen. Skt, new. Skt; struct sockaddr_in server. Name, client. Name; listen. Skt = socket(AF_INET, SOCK_STREAM, 0); //Fill in server. Name bind(listen. Skt, &server. Name, sizeof(server. Name)); listen(listen. Skt, 5); new. Skt = accept(listen. Skt, &client. Name, sizeof(client. Name)); // Fire off a thread to do communication using send and recv on new. Skt // Loop back and accept another connection close(skt); }

typedef struct myarg { int skt; int whatever; }My. Arg; … … pthread_attr_t *default

typedef struct myarg { int skt; int whatever; }My. Arg; … … pthread_attr_t *default = NULL; My. Arg *arg; while (!done) { new. Skt = accept(listen. Skt, &client. Name, sizeof(client. Name)); // Allocate and fill the argument structure arg = (My. Arg *)malloc(sizeof(My. Arg)); arg->skt = new. Skt; // Fire off a thread to do communication using send and recv on new. Skt pthread_create(&threads[j], default, Handle. Connection, (void *)arg); }

typedef struct myarg { int skt; int whatever; }My. Arg; … … void *Handle.

typedef struct myarg { int skt; int whatever; }My. Arg; … … void *Handle. Connection(void *arg) { int skt; char buffer[255]; // Don’t do this!!!! Allocate as much as you need! skt = (My. Arg *)arg->skt; recv(skt, buffer, bufflen, 0); // Wrap this in a while loop // Process the message send(skt, responselength, 0); }

Thread Issues § Thread function only gets One void * argument and void *

Thread Issues § Thread function only gets One void * argument and void * return How do you do more? § Reentrance § False Sharing Not same global variable, but within cache line § Mutual Exclusion

Effects of False Sharing Shared variables In Same Cache Line Local variables • Multiple

Effects of False Sharing Shared variables In Same Cache Line Local variables • Multiple Threads • Writing to two variables • Placement • local stack variables • Right next to each other • 16 words apart • 32 words apart

Synchronization Primitives § int pthread_mutex_init( pthread_mutex_t *mutex_lock, const pthread_mutexattr_t *lock_attr); § int pthread_mutex_lock( pthread_mutex_t

Synchronization Primitives § int pthread_mutex_init( pthread_mutex_t *mutex_lock, const pthread_mutexattr_t *lock_attr); § int pthread_mutex_lock( pthread_mutex_t *mutex_lock); § int pthread_mutex_unlock( pthread_mutex_t *mutex_lock); § int pthread_mutex_trylock( pthread_mutex_t *mutex_lock);

#include <pthread. h> void *find_min(void *list_ptr) pthread_mutex_t minimum_value_lock; int minimum_value, partial_list_size; main(){ minimum_value =

#include <pthread. h> void *find_min(void *list_ptr) pthread_mutex_t minimum_value_lock; int minimum_value, partial_list_size; main(){ minimum_value = MIN_INT; pthread_init(); pthread_mutex_init(&minimum_value_lock, NULL); /*inititalize lists etc, create and join threads*/ } void *find_min(void *list_ptr){ int *partial_list_ptr, my_min = MIN_INT, i; partial_list_ptr = (int *)list_ptr; for (i = 0; i < partial_list_size; i++) if (partial_list_ptr[i] < my_min) my_min = partial_list_ptr[i]; pthread_mutex_lock(minimum_value_lock); if (my_min < minimum_value) minimum_value = my_min; pthread_mutex_unlock(minimum_value_lock); pthread_exit(0); }

Locking Overhead § Serialization points Minimize the size of critical sections Be careful §

Locking Overhead § Serialization points Minimize the size of critical sections Be careful § Rather than wait, check if lock is available Pthread_mutex_trylock If already locked, will return EBUSY Will require restructuring of code

/* Finding k matches in a list */ void *find_entries(void *start_pointer) { /* This

/* Finding k matches in a list */ void *find_entries(void *start_pointer) { /* This is the thread function */ struct database_record *next_record; int count; current_pointer = start_pointer; do { next_record = find_next_entry(current_pointer); count = output_record(next_record); } while (count < requested_number_of_records); } int output_record(struct database_record *record_ptr) { int count; pthread_mutex_lock(&output_count_lock); output_count ++; count = output_count; pthread_mutex_unlock(&output_count_lock); if (count <= requested_number_of_records) print_record(record_ptr); return (count); }

/* rewritten output_record function */ int output_record(struct database_record *record_ptr) { int count; int lock_status;

/* rewritten output_record function */ int output_record(struct database_record *record_ptr) { int count; int lock_status; lock_status=pthread_mutex_trylock(&output_count_lock); if (lock_status == EBUSY) { insert_into_local_list(record_ptr); return(0); } else { count = output_count; output_count += number_on_local_list + 1; pthread_mutex_unlock(&output_count_lock); print_records(record_ptr, local_list, requested_number_of_records - count); } } return(count + number_on_local_list + 1);