Sie sind auf Seite 1von 23

CE 466

System Programming

Threads

Computer Engineering Department


Yarmouk University
11/30/2008

What is a Threads

• An encapsulation of a flow of control in a program


• A light weight process
• Shares an address space and other resources
• Cheaper context switching cost
• Multi threaded programs run through multiple code
paths simultaneously
• Natural for implementing tasks that can be broken
into subtasks that may run cooperatively

1
What is a Threads

• A thread is a semi-process, that has its own


stack, and executes a given piece of code
• Threads exists within a process
• Multiple strands of execution in a single
program
• A mechanism to allow a program do more than
one thing at a time (appear to run concurrently)
• Thread Group is a set of threads all executing
inside the same process

Processes vs. Threads

• Created threads share same


• Memory space (code, static and global variables, heap)
• Process attributes held by the kernel ( User ID, current
directory, file descriptors)
• Threads have their own:
• Registers and stack, ID, priority level
• Overhead of creating a new thread is significantly
less than when creating a new process
• Switching between threads requires much less work
than switching between processes

2
Processes vs. Threads

• Threads demand much less resources than


processes do
• Writing multithreaded programs could be
tricky:
• Subtle faults (timing, sharing variables)
• Thread synchronization and mutual exclusions is
the programmer responsibility
• Harder to debug a multithreaded program
• exec() in a process vs. a thread

Thread Execution

3
Thread Pros and Cons

• Pros:
• Responsiveness
• Resource Sharing
• Economy
• Utilization of MP Architectures
• Cons:
• Extra caution when programming threads
• Complexity of extending process concepts (e.g. when
forking a process should all threads be created? How are
signals handled?)

Multithreading Models

• Support for threads may be provided either at


the user or kernel levels:
• User Level: supported by the thread library
above the kernel (all management code and data
structures in user space)
• Kernel Level: supported directly by the operating
system (all management code and data structures
in the kernel space)
• A relation exists between the two

4
Multithreading Models

• Many-to-One:
• Many user-level threads mapped
to single kernel thread
• Managed in the user space by the
thread library (efficient)
• Used on systems that do not
support kernel threads
• Entire process blocks in case one
of its threads block (-ve)
• True concurrency is not fully
achieved (-ve)

Multithreading Models

• One-to-One:
• Each user-level thread maps to kernel thread
• A process does not block if one of its threads block
• Better utilizes multiprocessors (each thread runs on its
own processor)
• Too many threads consume system resources (-ve)

5
Multithreading Models

• Many-to-Many:
• Multiplexes user threads to a
smaller or equal number of
kernel threads
• Developer can create as many
user threads
• The corresponding kernel
threads can run in parallel on a
multiprocessor

Servers and Thread Pools

• A server receives requests and creates threads


to service these requests:
• Cost of creating a thread?
• Cost of discarding a thread?
• How many total threads can/should be created?
• Create a number of threads at process startup
and let them wait for work

6
Thread families

• Three families of mainstream threads are


available:
• POSIX-style: run on Unix (like) systems (e.g.
Pthreads)
• Microsoft-style threads: run on Windows (e.g.
Win32)
• Java threads: run on a variety of operating systems
• Other programming languages have their own
way of handling threads (Perl, Python,
Ruby,…)

POSIX Threads

• The idea of threads has been around for some


time but not standardized
• The IEEE POSIX committee published the
1003.1c specifications (thread creation and
synchronization)
• Linux implements the POSIX standard thread
API (Pthreads)

7
POSIX Threads

• The Pthread functions are not included in the


standard C librarybut in libpthread.a
• You should add “-lpthread” to the command
line when you link your program which
implements threads.

Thread Manipulation

1. Thread creation
• Each thread is identified by a thread ID
• Upon creation, a thread executes a function
• The function takes a single parameter of type void*
(can be used to pass data to the thread)
• The function returns void* (can be used to pass data
from thread back to creator process)
• Thread attributes control details of how the
thread interacts with the rest of the program

8
Thread Creation

NULL: default
thread ID is attributes
stored

int pthread_create (pthread_t *thread,


pthread_attr_t *attr,
void* (*start_routine) (void*),
void *arg)

thread function
Passed as argument to
thread function

Thread Creation

• The call to ‘pthread_create’ returns


immediately
• The original thread continues executing the
instructions following the call
• Meanwhile, the new thread begins executing
the thread function
• Linux schedules both threads asynchronously
• No order of execution is guaranteed

9
Thread Attributes

• pthread_attr_t object encapsulates properties such as:


stack size, scheduling policy, detach state,
cancelation state
• These attributes affect a thread only at the time of
creation attribute objects ……………
state ……………
stack ……………
pthread_attr_getstack
pthread_attr_setstack
Scheduling ……………
pthread_attr_getschedparam
pthread_attr_setschedparam
pthread_attr_getschedpolicy
pthread_attr_setschedpolicy
……………

Thread Manipulation

2. Thread termination
• Causes the current thread to exit and free any thread-
specific resources it is taking
Usually NULL, anything else
might be out of scope

int pthread_exit ( void *Value );

• Useful if we want to exit a thread in the middle of its


execution

10
Example 1
#include <pthread.h>
#include <stdio.h>

void* Display_0s (void* NoValue)


{
while(1)
fputc (‘0 ’,stderr);
return NULL;
}

Void main ()
{
pthread_t thread_id;
pthread_create (&thread_id,NULL,&Display_0s,NULL);
while(1)
fputc (‘1 ’,stderr);
}

Example 2
#include <stdio.h>

struct char_print_parms
{
char character;
int count;
};

void* Char-Print (void* params)


{
struct char_print_parms* p=(struct char_print_parms*)params;
int i, LocalCount;
LocalCount = p->count;
for (i = 0 ; i < LocalCount ; i++)
fputc (p->character, stderr);
return NULL;
}

11
Example 2
#include <pthread.h>
#include <stdio.h>
void main ()
{
pthread_t thread_1, thread_2;
struct char_print_parms thread_1_args;
struct char_print_parms thread_2_args;
thread_1_args.character =’0 ’;
thread_1_args.count = 1000;
pthread_create (&thread_1,NULL,&char_print,&thread_1_args);
thread_2_args.character =’1 ’;
thread_2_args.count = 2000;
pthread_create (&thread_2,NULL,&char_print,&thread_2_args);
}

Main thread may terminate before the other threads do:


Local variables will be deallocated and inaccessible by threads 1 and 2

Thread Manipulation

3. Joining threads
• Suspends the calling thread until target thread
terminates
thread ID to
wait for
int pthread_join( pthread_t thread,
void **thread_return)

receive the finished thread’s


return value (NULL if not
important)

12
Example 2
#include <pthread.h>
#include <stdio.h>
void main ()
{
pthread_t thread_1, thread_2;
struct char_print_parms thread_1_args;
struct char_print_parms thread_2_args;
thread_1_args.character =’0 ’;
thread_1_args.count = 1000;
pthread_create (&thread_1,NULL,&char_print,&thread_1_args);
thread_2_args.character =’1 ’;
thread_2_args.count = 2000;
pthread_create (&thread_2,NULL,&char_print,&thread_2_args);

pthread_join (thread_1,NULL);
pthread_join (thread_2,NULL);

Example 3
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
char message[] = “Hello World”;
void *thread_function(void *arg)
{
printf(“thread_function is running.
Argument was %s\n”, (char *)arg);
sleep(3);
strcpy(message, “Bye!”);
pthread_exit(“Thank you for the CPU
time”);
}

13
Example 3
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (res != 0) {
perror(“Thread creation failed”);
exit(EXIT_FAILURE);
}
printf(“Waiting for thread to finish...\n”);
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror(“Thread join failed”);
exit(EXIT_FAILURE);
}
printf(“Thread joined, it returned %s\n”, (char *)thread_result);
printf(“Message is now %s\n”, message);
exit(EXIT_SUCCESS);
}

Other Thread Functions


• Pthread_t pthread_self():
• Returns the id of the thread in which it is called.
• Pthread_t pthread_equal (pthread_t x,
pthread_t y):
• Compares a thread id (x) with another (y)
• int pthread_detach(pthread_t thread):
• Resources of a thread can be reclaimed when a thread terminates
(detached vs. joinable thread)
• int pthread_cancel(pthread_t thread)
• Cancels the thread ‘thread’
• A thread may be asynchronously cancelable; synchronously
cancelable; uncancelable

14
Example 4
#include <pthread.h>
void* thread_function (void* thread_arg)
{
/* Do work here... */
}
int main ()
{
pthread_attr_t attr;
pthread_t thread;
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
pthread_create (&thread, &attr, &thread_function, NULL);
pthread_attr_destroy (&attr);
/* Do work here... */
/* No need to join the second thread. */
return 0;
}

User vs. Kernel-Level Threads

User Kernel
User manages threads Kernel creates and manages
threads
A thread may monopolize Kernel schedules each
the time slice of the process thread within the time slice
of a process
Switching is fast Switching has more
overhead

15
Java Example
class SimpleThread extends Thread {
public SimpleThread(String str) {
super(str);
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {}
}
System.out.println("DONE! " + getName());
}
}

Java Example

class TwoThreadsTest {
public static void main (String
args[]) {
new
SimpleThread("Jamaica").start();
new
SimpleThread("Fiji").start();
}
}

16
Java Example
0 Jamaica
0 Fiji
1 Fiji
1 Jamaica
2 Jamaica
2 Fiji
3 Fiji
3 Jamaica
4 Jamaica
4 Fiji
5 Jamaica
5 Fiji
6 Fiji
6 Jamaica
7 Jamaica
7 Fiji
8 Fiji
9 Fiji
8 Jamaica
DONE! Fiji
9 Jamaica
DONE! Jamaica

Synchronization (Mutexes)

• Mutual Exclusion locks


• A special lock that only one thread may lock
at a time
• If a second thread tries to lock an already
locked mutex and then a the second thread is
put on hold (blocked)
• When the first thread unlocks the mutex the
second thread is allowed to resume execution
(unblocked)

17
Synchronization (Mutexes)

• #include <pthread.h>
• pthread_mutex_t mutex;
• int pthread_mutex_init(pthread_mutex_t
*mutex, const pthread_mutexattr_t
• *mutexattr);
• int pthread_mutex_lock(pthread_mutex_t
*mutex));
• int pthread_mutex_unlock(pthread_mutex_t
*mutex);
• int pthread_mutex_destroy(pthread_mutex_t
*mutex);

Example 5
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
pthread_mutex_t work_mutex; /* protects both work_area and
time_to_exit */
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;

18
Example 5
void *thread_function(void *arg) {
sleep(1);
pthread_mutex_lock(&work_mutex);
while(strncmp(“end”, work_area, 3) != 0) {
printf(“You input %d characters\n”, strlen(work_area) -1);
work_area[0] = ‘\0’;
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while (work_area[0] == ‘\0’ ) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = ‘\0’;
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}

Example 5
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex, NULL);
if (res != 0) {
perror(“Mutex initialization failed”);
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror(“Thread creation failed”);
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex);
printf(“Input some text. Enter ‘end’ to finish\n”);

19
Example 5
while(!time_to_exit) {
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
while(1) {
pthread_mutex_lock(&work_mutex);
if (work_area[0] != ‘\0’) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else {
break;
}
}
}
pthread_mutex_unlock(&work_mutex);
printf(“\nWaiting for thread to finish...\n”);
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror(“Thread join failed”);
exit(EXIT_FAILURE);
}
printf(“Thread joined\n”);
pthread_mutex_destroy(&work_mutex);
exit(EXIT_SUCCESS);
}

Synchronization (Semaphores)

#include <semaphore.h>
• int sem_init(sem_t *sem, int
pshared, unsigned int value);
• int sem_wait(sem_t * sem);
• int sem_post(sem_t * sem);
• int sem_destroy(sem_t * sem);

20
sem_init

int sem_init(sem_t *sem, int


pshared, unsigned int value);
• Initializes a semaphore object pointed to by
sem
• pshared parameter controls the type of
semaphore (0 means local to current process)
• value gives the semaphore an initial
integer value

Control Functions

int sem_wait(sem_t * sem);


• Atomically decreases the value of the
semaphore by 1 (waits until the semaphore has
a nonzero count first)
int sem_post(sem_t * sem);
• Atomically increases the value of the
semaphore by 1

21
Control Functions

int sem_wait(sem_t * sem);


• Atomically decreases the value of the
semaphore by 1 (waits until the semaphore has
a nonzero count first)

Example 5
sem_t bin_sem;

#define WORK_SIZE 1024


char work_area[WORK_SIZE];

void *thread_function(void *arg) {


sem_wait(&bin_sem);
while(strncmp(“end”, work_area, 3) != 0) {
printf(“You input %d characters\n”,
strlen(work_area) -1);
sem_wait(&bin_sem);
}
pthread_exit(NULL);
}

22
Example 5
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = sem_init(&bin_sem, 0, 0);
res = pthread_create(&a_thread, NULL,
thread_function, NULL);
printf(“Input some text. Enter ‘end’ to
finish\n”);
while(strncmp(“end”, work_area, 3) != 0) {
fgets(work_area, WORK_SIZE, stdin);
sem_post(&bin_sem);
}
printf(“\nWaiting for thread to finish...\n”);
res = pthread_join(a_thread, &thread_result);
printf(“Thread joined\n”);
sem_destroy(&bin_sem);
exit(EXIT_SUCCESS);
}

23