One way to reduce context switch time is to run lightweight processes within the same address space. This abstraction is often called a thread. Look at Figure 12-1.
Global variables and resources are shared between threads within the same process. Each thread has its own stack.
What system uses threads? Mach, Xinu, OSF/1 (Digital Unix), Java Virtual Machine, almost all new OSes.
Have pthreads package on our system (must use cc and c++ to compile). Run-time library.
/* in /cs/cs3013/public/example/mutexthr.c */
#include <stdio.h>
#include <pthread.h>
/* cc -o mutexthr mutexthr.c -lpthread */
pthread_mutex_t mutex; /* mutex id */
main()
{
pthread_t idA, idB; /* ids of threads */
void *MyThread(void *);
if (pthread_mutex_init(&mutex, NULL) < 0) {
perror("pthread_mutex_init");
exit(1);
}
if (pthread_create(&idA, NULL, MyThread, (void *)"A") != 0) {
perror("pthread_create");
exit(1);
}
if (pthread_create(&idB, NULL, MyThread, (void *)"B") != 0) {
perror("pthread_create");
exit(1);
}
(void)pthread_join(idA, NULL);
(void)pthread_join(idB, NULL);
(void)pthread_mutex_destroy(&mutex);
}
int x = 0; /* global shared variable */
void *MyThread(void *arg)
{
char *sbName;
sbName = (char *)arg;
IncrementX();
printf("X = %d in Thread %s\n", x, sbName);
}
IncrementX()
{
int Temp; /* local variable */
BeginRegion(); /* enter critical region */
Temp = x;
Temp = Temp + 1;
x = Temp;
EndRegion(); /* exit critical region */
}
BeginRegion()
{
pthread_mutex_lock(&mutex);
}
EndRegion()
{
pthread_mutex_unlock(&mutex);
}
/* in /cs/cs3013/public/example/pcthreads.c */
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
/* cc -o pcthreads pcthreads.c -lpthread -lrt */
sem_t produced, consumed; /* semaphores */
int n;
main()
{
pthread_t idprod, idcons; /* ids of threads */
void *produce(void *);
void *consume(void *);
int loopcnt = 5;
n = 0;
if (sem_init(&consumed, 0, 0) < 0) {
perror("sem_init");
exit(1);
}
if (sem_init(&produced, 0, 1) < 0) {
perror("sem_init");
exit(1);
}
if (pthread_create(&idprod, NULL, produce, (void *)loopcnt) != 0) {
perror("pthread_create");
exit(1);
}
if (pthread_create(&idcons, NULL, consume, (void *)loopcnt) != 0) {
perror("pthread_create");
exit(1);
}
(void)pthread_join(idprod, NULL);
(void)pthread_join(idcons, NULL);
(void)sem_destroy(&produced);
(void)sem_destroy(&consumed);
}
void *produce(void *arg)
{
int i, loopcnt;
loopcnt = (int)arg;
for (i=0; i<loopcnt; i++) {
sem_wait(&consumed);
n++; /* increment n by 1 */
sem_post(&produced);
}
}
void *consume(void *arg)
{
int i, loopcnt;
loopcnt = (int)arg;
for (i=0; i<loopcnt; i++) {
sem_wait(&produced);
printf("n is %d\n", n); /* print value of n */
sem_post(&consumed);
}
}
/* in /cs/cs3013/public/example/pcthreads.C */
#include <iostream.h>
#include <pthread.h>
#include <semaphore.h>
/* c++ -o pcthreads pcthreads.C -lpthread -lrt */
sem_t produced, consumed; /* semaphores */
int n;
main()
{
pthread_t idprod, idcons; /* ids of threads */
void *produce(void *);
void *consume(void *);
int loopcnt = 5;
n = 0;
if (sem_init(&consumed, 0, 0) < 0) {
perror("sem_init");
exit(1);
}
if (sem_init(&produced, 0, 1) < 0) {
perror("sem_init");
exit(1);
}
if (pthread_create(&idprod, NULL, produce, (void *)loopcnt) != 0) {
perror("pthread_create");
exit(1);
}
if (pthread_create(&idcons, NULL, consume, (void *)loopcnt) != 0) {
perror("pthread_create");
exit(1);
}
(void)pthread_join(idprod, NULL);
(void)pthread_join(idcons, NULL);
(void)sem_destroy(&produced);
(void)sem_destroy(&consumed);
}
void *produce(void *arg)
{
int i, loopcnt;
loopcnt = (int)arg;
for (i=0; i<loopcnt; i++) {
sem_wait(&consumed);
n++; /* increment n by 1 */
sem_post(&produced);
}
}
void *consume(void *arg)
{
int i, loopcnt;
loopcnt = (int)arg;
for (i=0; i<loopcnt; i++) {
sem_wait(&produced);
cout << "n is " << n << "\n"; /* print value of n */
sem_post(&consumed);
}
}
Java has a pre-defined thread object. Can write new threaded objects by extending this class in Java. Threads can be assigned different priorities.
// in /cs/cs3013/public/example/MutexThr.java
// javac MutexThr.java
// java MutexThr
public class MutexThr {
public static void main(String[] args) {
SharedX xobj = new SharedX(); // create shared object
MyThread thrA = new MyThread("A", xobj); // create A thread
MyThread thrB = new MyThread("B", xobj); // create B thread
thrA.start(); // start A thread
thrB.start(); // start B thread
try {
thrA.join(); // wait for A to finish
thrB.join(); // wait for B to finish
} catch (InterruptedException e) {
System.out.println("Join interrupted");
}
}
}
// in /cs/cs3013/public/example/MyThread.java
public class MyThread extends Thread {
private String myName; // string identifier
private SharedX xobj; // reference to shared object
public MyThread(String whoami, SharedX xobjarg) {
myName = whoami;
xobj = xobjarg;
}
public void run() {
xobj.IncrementX();
System.out.println("X = " + xobj.ReadX() + " in Thread " + myName);
}
}
// in /cs/cs3013/public/example/SharedX.java
public class SharedX {
public int x;
public SharedX() {
x = 0; // initialize X to zero
}
// use synchronized to prohibit concurrent access of x
public synchronized void IncrementX() {
int Temp; // local variable
Temp = x;
Temp = Temp + 1;
x = Temp;
}
public synchronized int ReadX() {
return x; // return current value of x
}
}
// in /cs/cs3013/public/example/PCExample.java
// javac PCExample.java
// java PCExample
public class PCExample {
public static void main(String[] args) {
SharedN nobj = new SharedN(); // create shared object
PCThread thrP = new PCThread("P", nobj); // create producer thread
PCThread thrC = new PCThread("C", nobj); // create consumer thread
thrP.start(); // start producer thread
thrC.start(); // start consumer thread
try {
thrP.join(); // wait for producer to finish
thrC.join(); // wait for consumer to finish
} catch (InterruptedException e) {
System.out.println("Join interrupted");
}
}
}
// in /cs/cs3013/public/example/PCThread.java
public class PCThread extends Thread {
private String myName; // string identifier
private SharedN nobj; // reference to shared object
public PCThread(String whoami, SharedN nobjarg) {
myName = whoami;
nobj = nobjarg;
}
public void run() {
int i;
int loopcnt = 5;
if (myName.equals("P")) {
// producer thread
for (i=0; i<loopcnt; i++) {
nobj.IncrementN();
}
}
else {
// consumer thread
for (i=0; i<loopcnt; i++) {
System.out.println("n is " + nobj.ReadN());
}
}
}
}
// in /cs/cs3013/public/example/SharedN.java
public class SharedN {
public int n;
int cValueAvail = 1; // initially use one value
public SharedN() {
n = 0; // initialize N to zero
}
// use synchronized and conditions to control access to N
public synchronized void IncrementN() {
while (cValueAvail > 0) {
try {
wait(); // wait for value to be consumed
} catch (InterruptedException e) {
System.out.println("Wait interrupted");
}
}
n++;
cValueAvail = 1;
notifyAll(); // could also use notify() if only one is waiting
}
public synchronized int ReadN() {
while (cValueAvail == 0) {
try {
wait(); // wait for value to be produced
} catch (InterruptedException e) {
System.out.println("Wait interrupted");
}
}
cValueAvail = 0;
notifyAll(); // could also use notify() if only one is waiting
return n; // return value of n (will continue after notify)
}
}