You are on page 1of 8

Lab 6: Threading using Pthreads API

Threading using Pthreads API


Recall the Fig. 01 from the theory.

Figure 1

Recall that fork() also creates a child process, but the child process has its own address space. Threads are
considered lightweight processes because they share the same address space of the parent process as
shown in Fig. 01. As a result threads have advantages over child processes created through fork() such as
quick creation and termination, quicker context switching, less communication overhead between
threads.

C provides an API called Pthreads to create and destroy threads. Observe the following program. The
program only

#include <stdio.h> /* standard I/O routines */

#include <pthread.h> /* pthread functions and data structures */

/* function to be executed by the new thread */

void* PrintHello(void* data)

int my_data = (int)data; /* data received by thread */

pthread_detach(pthread_self());

Page 1 of 8
printf("Hello from new thread - got %d\n", my_data);

pthread_exit(NULL); /* terminate the thread */

/*program's execution begins in main */

int main(int argc, char* argv[])

int rc; /* return value */

pthread_t thread_id; /* thread's ID (just an integer) */

int t = 11; /* data passed to the new thread */

/* create a new thread that will execute 'PrintHello' */

rc = pthread_create(&thread_id, NULL, PrintHello, (void*)t);

if(rc) /* could not create thread */

printf("\n ERROR: return code from pthread_create is %d \n", rc);

exit(1);

printf("\n Created new thread (%u) ... \n", thread_id);

pthread_exit(NULL); /* terminate the thread */

How it works
- In main(), a variable called thread_id is declared, which is of type pthread_t. This is an integer
value used to identify the thread in the system.

- After declaring thread_id, we call the pthread_create() function to create a real, living thread. It
takes 4 arguments.
o The first argument is a pointer to thread_id that supplies the program with the thread's
identifier.

Page 2 of 8
o The second argument is used to set some attributes for the new thread. The NULL pointer
uses the default values.
o The third argument is the main function for the thread; the thread begins executing user
code at this address. In this case it is the function called PrintHello().
o Notice that PrintHello() accepts a void * as an argument and also returns a void * as a
return value. This shows us that it is possible to use a void * to pass an arbitrary piece of
data to our new thread, and that our new thread can return an arbitrary piece of data
when it finishes. How do we pass our thread an arbitrary argument? We use the fourth
argument to the pthread_create() call. If we do not want to pass any data to the new
thread, we set the fourth argument to NULL. pthread_create() returns zero on success
and a non-zero value on failure.
- If the pthread_create() returns successfully, then the program consists of two threads. This is
because the main program is also a thread and it executes the code in the main() function in
parallel to the thread it creates.
- The call to pthread_exit causes the current thread to exit and free any thread-specific resources
it is taking.

Passing arguments (structures) to threads


If multiple arguments are required by threads, then structures are used as arguments. The following
example shows how structures can be passed to threads. Execute the program and observe the output.

#include <iostream>

#include <cstdlib>

#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

struct thread_data {

int thread_id;

char *message;

};

Page 3 of 8
void *PrintHello(void *threadarg) {

struct thread_data *my_data;

my_data = (struct thread_data *) threadarg;

cout << "Thread ID : " << my_data->thread_id ;

cout << " Message : " << my_data->message << endl;

pthread_exit(NULL);

int main () {

pthread_t threads[NUM_THREADS];

struct thread_data td[NUM_THREADS];

int rc;

int i;

for( i = 0; i < NUM_THREADS; i++ ) {

cout <<"main() : creating thread, " << i << endl;

td[i].thread_id = i;

td[i].message = "This is message";

rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);

if (rc) {

cout << "Error:unable to create thread," << rc << endl;

exit(-1);

pthread_exit(NULL);

Page 4 of 8
}

Task 2: Understanding the thread pool


You have studied various approaches in the OS theory for creating threads. Thread pool is one of the
approaches used for creating threads. In thread pool, a predefined number of threads are created to save
thread creation time upon arrival of a new job.

Now that you have created threads and passed structures as arguments to the threads, your task is to
understand the code below and submit a line-by-line explanation of the program to show how thread
pools are implemented.

#include <stdio.h>

#include <string.h>

#include <pthread.h>

#include <stdlib.h>

#include <unistd.h>

#include <time.h>

#define THREAD_NUM 4

typedef struct Task {

int a, b;

} Task;

Task taskQueue[256];

int taskCount = 0;

pthread_mutex_t mutexQueue;

pthread_cond_t condQueue;

void executeTask(Task* task) {

Page 5 of 8
usleep(50000);

int result = task->a + task->b;

printf("The sum of %d and %d is %d\n", task->a, task->b, result);

void submitTask(Task task) {

pthread_mutex_lock(&mutexQueue);

taskQueue[taskCount] = task;

taskCount++;

pthread_mutex_unlock(&mutexQueue);

pthread_cond_signal(&condQueue);

// 1 2 3 4 5

// 2 3 4 5

void* startThread(void* args) {

while (1) {

Task task;

pthread_mutex_lock(&mutexQueue);

while (taskCount == 0) {

pthread_cond_wait(&condQueue, &mutexQueue);

task = taskQueue[0];

int i;

for (i = 0; i < taskCount - 1; i++) {

taskQueue[i] = taskQueue[i + 1];

Page 6 of 8
}

taskCount--;

pthread_mutex_unlock(&mutexQueue);

executeTask(&task);

int main(int argc, char* argv[]) {

pthread_t th[THREAD_NUM];

pthread_mutex_init(&mutexQueue, NULL);

pthread_cond_init(&condQueue, NULL);

int i;

for (i = 0; i < THREAD_NUM; i++) {

if (pthread_create(&th[i], NULL, &startThread, NULL) != 0) {

perror("Failed to create the thread");

srand(time(NULL));

for (i = 0; i < 100; i++) {

Task t = {

.a = rand() % 100,

.b = rand() % 100

};

submitTask(t);

for (i = 0; i < THREAD_NUM; i++) {

if (pthread_join(th[i], NULL) != 0) {

Page 7 of 8
perror("Failed to join the thread");

pthread_mutex_destroy(&mutexQueue);

pthread_cond_destroy(&condQueue);

return 0;

Page 8 of 8

You might also like