Hello C programmers! I'm a novice programmer about to graduate as long as I can complete this assignment by 10:00 AM tomorrow.
The assignment is a solution to the producer-consumer problem using a bounded buffer and multiple processes (as opposed to threads, which I had to do in an earlier assignment). Unfortunately for me, the assignments in this class are written in C, a language I don't fully understand as much as my peers (my University started teaching beginning programming classes in C the semester after I was finished with them, I learned in Java).
Anyway, to the problem. The program is to take in a file using stdin, and use multiple processes to rearrange the lines of the file based on the line number at the beginning of each line (they are currently out of order).
The producer takes each line, and creates a struct "item" which has an int id and a char[] str. It then places the item in the bounded buffer for the consumer to use. I will include the code for each module of the program, but the errors all seem to come from producer.c, where my curItem to which I try and assign values from the file keeps coming up with a needpointer error.
Also, I haven't yet successfully compiled it, so I'm not sure if the output will be what I want, so if you can catch any other errors and let me know that would be greatly appreciated.
producer.c
Code:
#include "assign5.h"
/* Global variables for producer.c */
pid_t prodid; /* Producer Process Id */
void *shmptr; /* pointer to shared memory returned by shmget() */
shr_mem_t *sp; /* pointer to shared memory structure */
int shmid; /* shared memory ID number */
sem_t *MUTEXptr; /* pointer to the MUTEX semaphore */
sem_t *EMPTYptr; /* pointer to the EMPTY semaphore */
sem_t *FULLptr; /* pointer to the FULL semaphore */
/*******************************************************************/
/* Function to cleanup before exiting */
/*******************************************************************/
void cleanup_on_exit()
{
/* Only detach shared memory. Parent will remove it from system */
/* Don't worry about semaphores. Parent will delete them */
shmdt(shmptr);
}
/**************************************************************************/
/* Function to be invoked by the OS if CTRL-C is pressed during execution */
/**************************************************************************/
void ctrl_c(int signum)
{
perror("producer: You have pressed CTRL_C\n");
cleanup_on_exit(); /* Perform cleanup operations before exiting */
exit(0);
}
/**************************************************************************/
/* Function to produce one item in the buffer */
/**************************************************************************/
void produce_item(struct item *item)
{
sem_wait(EMPTYptr); /* wait for a full buffer */
sem_wait(MUTEXptr); /* wait for the mutex lock */
sp->buff[sp->in].id = item->id; /* put item id into buffer */
sp->buff[sp->in].str = item->str; /* .... Place associated string into buffer item istring .... */
printf("Producer: Produced %10d at buffer[%d]\n", item->id, sp->in);
sp->in = (sp->in + 1) % BUFFSIZE; /* increment buffer index */
sem_post(MUTEXptr); /* release the mutex lock */
sem_post(FULLptr); /* signal a full buffer slot */
}
/**************************************************************************/
/* Main producer routine */
/**************************************************************************/
int main(int argc, char *argv[])
{
int n; /* local variable for produced item */
key_t shm_key; /* key for shared memory segment */
char *procName = argv[0]; /* name of this process */
/* local variables for reading in lines and putting into item structure */
int c, charIndex, numIndex, lineId;
char curStr[80], idStr[4];
struct item * curItem;
/* Setup system environment */
signal(SIGINT, ctrl_c); /* specify routine to handle CTRL-C */
setbuf(stdout, NULL); /* turn off buffering of stdout */
setbuf(stderr, NULL); /* turn off buffering of stderr */
/* Use ftok() to get value for the key to identify the shared memory segment */
shm_key = ftok(KEYPATH, KEYPROJ);
/* Get the ID of the existing shared memory segment */
shmid = shmget(shm_key, size_of(sp), S_IWUSR);
if (shmid == -1) {
perror("shmget failed in producer");
exit(1);
}
/* Attach the shared memory segment */
shmptr = shmat(shmid, NULL, 0);
if (shmptr == (void *)-1) {
perror("shmat failed in producer");
exit(1);
}
/* Cast shared memory pointer to structure type */
sp = (shr_mem_t *)shmptr;
/* Get pointers to the semaphores created by the parent */
MUTEXptr = sem_open(sp->MUTEXname, 0, 0, 0);
EMPTYptr = sem_open(sp->EMPTYname, 0, 0, 0);
FULLptr = sem_open(sp->FULLname, 0, 0, 0);
/* Read data and produce items until EOF */
charIndex = 0;
while ( (c=getchar()) != EOF ) { /* read lines of data fron stdin */
/* ... read new data item (id and string) from stdin ... */
if( c != '\n' && c != '\r' ){
curStr[charIndex++] = c;
}else{ /* hit the newline */
/* end the string */
curStr[charIndex] = '\0';
/* get first four chars and put in idStr */
for(charIndex = 0, numIndex = 0; charIndex < 4; charIndex++){
/* skip white spaces */
if( !isspace( curStr[charIndex] ) ){ idStr[numIndex++] = curStr[charIndex]; }
}
idStr[numIndex] = '\0';
/* convert idStr to int */
lineId = atoi(idStr);
/* get rid of id from curStr */
for(charIndex = 0; curStr[charIndex + 4] != '\0'; charIndex++){
curStr[charIndex] = curStr[charIndex + 4];
}
curStr[charIndex] = '\0';
/* assign id and string to new item */
curItem->id = lineId;
curItem->str = curStr;
/* validate item id values for correctness */
if(curItem->id >= MAX_STRINGS){
printf("***ERR*** Producer ignoring item with id=%d", item->id);
}else{
produce_item(curItem); /* add item to buffer */
}
}
}
/* At EOF ... */
curItem->id = SENTINEL;
curItem->str = '\0';
/* Add a sentinal item in Buffer to signal end of data input */
produce_item(curItem ); /* produce a sentinel value after EOF */
/* Write a termination message */
sem_wait(MUTEXptr); /* protect printf as a critical section */
printf("%s exiting.....\n", procName);
sem_post(MUTEXptr);
/* Clean up and exit */
cleanup_on_exit();
exit(0);
}
parent.c
Code:
#include "assign5.h"
/* Global variables for parent.c */
int shmid, i; /* the shared memory segment ID number */
void *shmptr; /* the pointer to shared memory returned by shmget() */
char MUTEXid[32]; /* the unique generated name for the MUTEX semaphore */
char EMPTYid[32]; /* the unique generated name for the EMPTY semaphore */
char FULLid[32]; /* the unique generated name for the FULL semaphore */
/*******************************************************************/
/* Function to cleanup shared memory and semaphores before exiting */
/*******************************************************************/
void cleanup_on_exit()
{
shmdt(shmptr); /* detach the shared memory */
shmctl(shmid, IPC_RMID, NULL); /* delete the shared memory segment */
sem_unlink(MUTEXid); /* delete the MUTEX semaphore */
sem_unlink(EMPTYid); /* delete the EMPTY semaphore */
sem_unlink(FULLid); /* delete the FULL sempahore */
}
/**************************************************************************/
/* Function to be invoked by the OS if CTRL-C is pressed during execution */
/**************************************************************************/
void ctrl_c(int signum)
{
printf("Parent: You have pressed CTRL-C\n");
cleanup_on_exit();
exit(0);
}
/**************************************************************************/
/* Main program function */
/**************************************************************************/
int main(int argc, char *argv[]) {
/* Local variable declarations */
key_t shm_key; /* key for shared memory segment */
shr_mem_t *sp; /* pointer to shared memory structure */
int pid; /* process ID number */
/* Setup system environment */
signal(SIGINT, ctrl_c); /* specify routine to handle CTRL-C */
setbuf(stdout, NULL); /* turn off buffering of stdout */
setbuf(stderr, NULL); /* turn off buffering of stderr */
/* Generate unique sempaphore names and initialize the semaphores */
pid = getpid(); /* get process id for semaphore names */
sprintf(MUTEXid,"semmutex%d", pid); /* generate MUTEX semaphore name */
sprintf(EMPTYid,"semempty%d", pid); /* generate EMPTY semaphore name */
sprintf(FULLid,"semfull%d", pid); /* generate FULL semaphore name */
/* Why set the permissions to 660? Is this a good choice? */
sem_open(MUTEXid, O_CREAT, 0660, 1); /* init the MUTEX semaphore */
sem_open(EMPTYid, O_CREAT, 0660, BUFFSIZE); /* init the EMPTY semaphore */
sem_open(FULLid, O_CREAT, 0660, 0); /* init the FULL semaphore */
/* Use ftok() to get a value for a key to identify a shared memory segment */
shm_key = ftok(KEYPATH, KEYPROJ);
/* Create the shared memory segment */
shmid = shmget(shm_key, size_of(sp), S_IRUSR | S_IWUSR);
if (shmid == -1) {
perror("shmget failed in parent");
exit(1);
}
/* Attach shared memory segment to the parent process */
shmptr = shmat(shmid, NULL, 0);
if (shmptr == (void *)-1) {
perror("shmat failed in parent");
cleanup_on_exit(); /* clean up before exiting */
exit(1);
}
/* Cast the returned pointer to the structure type */
sp = (shr_mem_t *)shmptr;
/* Initialize shared memory fields */
sp->debug = 0; /* Turn debug flag off */
sp->in = 0; /* index of next empty slot */
sp->out = 0; /* index of next full slot */
strcpy(sp->MUTEXname, MUTEXid); /* MUTEX semaphore name */
strcpy(sp->EMPTYname, EMPTYid); /* EMPTY semaphore name */
strcpy(sp->FULLname, FULLid); /* FULL semaphore name */
/* Initialize string array to be empty. WHY? */
for (i=0; i<MAX_STRINGS; i++) sp->strarray[i][0] = '\0'; /* null string */
/* Fork the producer and consumer processes */
printf("Creating the producer and consumer processes...\n");
if (fork() == 0) { /* in producer process */
/* Replace this program with producer program */
if (execl("producer", "Producer", NULL) == -1) {
perror("execl failed for producer");
cleanup_on_exit(); /* clean up before exiting */
exit(3);
}
}
if (fork() == 0) { /* in consumer process */
/* Replace this program with the consumer program */
if (execl("consumer", "Consumer", NULL) == -1) {
perror("execl failed for consumer");
cleanup_on_exit(); /* clean up before exiting */
exit(3);
}
}
/* Wait for producer and consumer processes to finish */
/* Is there something else you should check here ? If so, what is it? */
/* How would this change if you had multiple consumers ? */
wait(NULL);
wait(NULL);
/* Print termination message, clean up, and exit */
/* ... completed processing - print out contents of string array ... */
for (i = 0; (strarray[i][0] != '\0') && ( i < MAX_STRINGS ); i++){
printf("%s\n", strarray[i]);
}
printf("Parent process exiting.\n");
cleanup_on_exit(); /*Remove shared memory and semaphores */
return(0);
}
consumer.c
Code:
#include "assign5.h"
/* Global variables for consumer.c */
pid_t consid; /* Consumer Process Id */
void *shmptr; /* pointer to shared memory returned by shmget() */
/*******************************************************************/
/* Function to cleanup before exiting */
/*******************************************************************/
void cleanup_on_exit()
{
/* Only detach shared memory. Parent will remove it from system */
/* Don't worry about semaphores. Parent will delete them */
shmdt(shmptr);
}
/**************************************************************************/
/* Function to be invoked by the OS if CTRL-C is pressed during execution */
/**************************************************************************/
void ctrl_c(int signum)
{
perror("consumer: You have pressed CTRL_C\n");
cleanup_on_exit(); /* Perform cleanup operations before exiting */
exit(0);
}
/**************************************************************************/
/* Main Consumer Routine */
/**************************************************************************/
int main(int argc, char *argv[])
{
int n; /* local variable for consumed item */
int shmid; /* shared memory ID number */
key_t shm_key; /* key for shared memory segment */
shr_mem_t *sp; /* pointer to shared memory structure */
sem_t *MUTEXptr; /* pointer to the MUTEX semaphore */
sem_t *EMPTYptr; /* pointer to the EMPTY semaphore */
sem_t *FULLptr; /* pointer to the FULL semaphore */
char *procName = argv[0]; /* name of this process */
signal(SIGINT, ctrl_c);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
/* Use ftok() to get value for the key to identify the shared memory segment */
shm_key = ftok(KEYPATH, KEYPROJ);
/* Get the ID of the existing shared memory segment */
shmid = shmget(shm_key, size_of(sp), S_IRUSR);
if (shmid == -1) {
perror("shmget failed in consumer");
exit(1);
}
/* Attach the shared memory segment */
shmptr = shmat(shmid, NULL, 0);
if(shmptr == (void *)-1) {
perror("shmat failed in consumer");
exit(1);
}
/* Cast shared memory pointer to structure type */
sp = (shr_mem_t *)shmptr;
/* Get pointers to the semaphores created by the parent */
MUTEXptr = sem_open(sp->MUTEXname, 0, 0, 0);
EMPTYptr = sem_open(sp->EMPTYname, 0, 0, 0);
FULLptr = sem_open(sp->FULLname, 0, 0, 0);
/* Consume items until the SENTINEL is found */
do {
sem_wait(FULLptr); /* wait for a full buffer */
sem_wait(MUTEXptr); /* wait for the mutex lock */
n = sp->buff[sp->out].id; /* get item id from buffer */
/* Note that an item has been consumed and add PID to msg */
printf(" %s: Consumed %10d at buffer[%d]\n", procName, n, sp->out);
if (n != SENTINEL) {
/* .... place item string into string array .... */
sp->out = (sp->out + 1) % BUFFSIZE; /* increment buffer index */
}
sem_post(MUTEXptr); /* release the mutex lock */
sem_post(EMPTYptr); /* signal an empty buffer slot */
} while(n != SENTINEL);
/* Write a termination message */
sem_wait(MUTEXptr); /* protect printf as a critical section */
printf(" %s exiting....\n", procName);
sem_post(MUTEXptr);
/* Clean up and exit */
cleanup_on_exit();
exit(0);
}
assign5.h
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <errno.h>
#include <memory.h>
#include <ctype.h>
#define NUM_CONSUMERS 1
#define BUFFSIZE 14
#define MAX_STRINGS 110
#define SENTINEL -999999999 /* sentinel value for consumer process */
/* Bounded Buffer item structure */
struct item{
int id; /* string index value */
char str[80]; /* string value */
};
/* Structure for the shared memory region */
typedef struct{
int debug; /* debug flag */
int in; /* index of next empty slot */
int out; /* index of next full slot */
char MUTEXname[32]; /* name of the MUTEX semaphore */
char EMPTYname[32]; /* name of the EMPTY semaphore */
char FULLname[32]; /* name of the FULL semaphore */
struct item buff[BUFFSIZE]; /* circular buffer for producer/consumer items*/
char strarray[MAX_STRINGS][80]; /* shared array of strings for consumers */
} shr_mem_t;
/* Values for obtaining a shmid key via ftok() */
#define KEYPATH "."
#define KEYPROJ 4520
as5.data (input file)
Code:
107 Wow, you wasted an entire section on that? Yep, I can't stress enough how
63 * X-to-Y model. The mapping between LWPs and Threads.
22 We will dive into the world of threads with some a little bit of "theory"
23 first. We will examine thread synchronization primitives and then a tutorial
56 Before we can dive into the details of how threads are supported, we need to
30 ####################################################################
39 ####################################################################
3 The Beginning of the End
110 $$$$$$$$$$$$$$$$$$The End of the Beginning$$$$$$$$$$$$$$$$$$$$$$$$$$
47 ####################################################################
55 ####################################################################
76 Linux uses the one-to-one model. Each thread is mapped to a single LWP. Why
72 Solaris uses the many-to-many model. CPUs are mapped to any number of LWPs
69 * Unbound threads have process contention scope, in other words, these
87 thread type for the job.
17 However, times have changed and many papers have been written on
102 Yes, threads are great... for the right tasks! Don't waste your time
90 * Thread-safe means that the program protects shared data, possibly
77 is this approach favored in Linux over the many-to-many model? Linux LWPs are
91 through the use of mutual exclusion
40 Now that we have pictures of dancing needles in our heads, lets move onto
29 Isn't that something you put through an eye of a sewing needle? Yes.
54 different technique to support threads.
50 not support threads. Fortunately most modern Operating Systems support
53 few, support multithreaded programs. However, each Operating System uses a
62 also sometimes referred to as kernel threads.
18 multithreading. Some advocate the use of threads, while others do not. With
74 for slices of CPU time.
81 "processes" scheduled which are in the same thread family as the currently
92 * Reentrant code means that a program can have more than one thread
12 a program on a uniprocessor machine in most cases does not yield enough
104 event-based programming is the better route, or just plain, sequential, and
1 Multithreaded Programming :: Improving Performance through Threads
45 blown processes, but are smaller portions of the process running concurrently
82 running process.
108 important picking the right tool for the job is.
15 Parallelizing our thoughts does not come naturally nor is it an easy task.
37 doing other useful work even if the other needle took 4 hours to sew on a
5 sequential or serialized? Simply put, code is executed one instruction after
86 either one. Each scenario demands a thorough analysis to select the right
11 dominance of uniprocessing machines available to programmers. Multithreading
32 Think of sewing needles as the CPUs (or LWPs) and the threads in a
60 number of LWPs is usually greater than the number of CPUs in the system.
97 subset of Concurrency. Parallelism implies simultaneous running of code
6 the next in a monolithic fashion, with no regard to the many possible
35 both needles at the same time. Taking this analogy a little further, if one
85 your multithreaded program. There is no general rule when it comes to using
41 something more concrete. A thread is a sequence of instructions that can be
99 that many tasks can run in any order and possibly in parallel.
34 take longer to finish the job than if you split the thread into two and used
59 * Lightweight Process (LWP) can be thought of as a virtual CPU where the
78 really lightweight and thus LWP creation is not as expensive as it is in
61 Thread libraries communicate with LWPs to schedule threads. LWPs are
101 Part IV :: Threads Rule!
10 Why is it that most programs are sequential? One guess could be the relative
98 (which is impossible on uniprocessor machines) while Concurrency implies
27 What is a thread?
111$$$$ What is a process?
42 executed in parallel with other threads [wikipedia.com]. They are not
46 (or in parallel). Hence, the term lightweight is used.
80 multiprocess in Linux is that the scheduler (2.4) gives a 1 point boost to
19 the increasing popularity of Symmetric-Multiprocessing machines, programming
48 Part II :: Operating System Support
89 Part III :: Other Terms
8 degraded if the program performs a blocking call.
28 Part I :: Definition
24 on how to use POSIX pthreads. Finally, we will finish off with thread
36 needle had to sew on a button (blocking I/O), the other needle could continue
13 performance gains to merit days, weeks, or months worth of work to thread
52 Solaris, FreeBSD, Linux, AIX, HP-UX, IRIX, and Windows NT, just to name a
57 get familiarized with a few terms.
51 threads, either with their own thread library or through POSIX pthreads. Sun
31 How does it relate to programming then?
44 recursive definition but it makes sense. Threads of a program are not full-
84 Moreover, creating bound or unbound threads can greatly impact performance of
64 * Contention Scope is how threads compete for system resources (e.g.
67 threads contend with other processes on the entire system (and thus are
93 executing concurrently
96 * Concurrency vs. Parallelism - They are not the same! Parallelism is a
4 Most code written today is sequential. What do we mean by the term
20 multithreaded code is a skill worth learning.
105 no-frills code can do the job just right.
49 You cannot expect a multithreaded program to run on a kernel that does
95 (e.g. can be called from a signal handler)
68 scheduled by the kernel)
38 single button. If you only used one needle, you would be ~4 hours behind!
43 processes, but rather lightweight threads of execution. This seems like a
66 * Bound threads have system-wide contention scope, in other words, these
7 resources available to the program. Overall performance can be serverely
94 * Async-safe means that a function is reentrant while handling a signal
33 program as the fiber. If you had two needles but only one thread, it would
73 which are then mapped to any number of threads. The kernel schedules the LWPs
70 threads are scheduled by the library onto available LWPs
103 multithreading a program that isn't worth multithreading. Sometimes
14 code. Another guess is that most of us think in a sequential manner.
79 Solaris. Another bonus to make your program multithreaded rather than
25 performance and a brief overview of multiprocess programming.
100
106
16
21
26
2 (http://vergil.chemistry.gatech.edu/resources/programming/threads.htm)
3
30
39
47
55
58
65 scheduling)
71
75
83
88
9
desired output:
Code:
Creating the producer and consumer processes...
Producer[1106700]: Produced 107 at buffer[0]
Producer[1106700]: Produced 63 at buffer[1]
Producer[1106700]: Produced 22 at buffer[2]
Consumer[1104366]: Consumed 107 at buffer[0]
Producer[1106700]: Produced 23 at buffer[3]
Consumer[1104366]: Consumed 63 at buffer[1]
Producer[1106700]: Produced 56 at buffer[4]
Consumer[1104366]: Consumed 22 at buffer[2]
Producer[1106700]: Produced 30 at buffer[5]
Consumer[1104366]: Consumed 23 at buffer[3]
Producer[1106700]: Produced 39 at buffer[6]
Consumer[1104366]: Consumed 56 at buffer[4]
Producer[1106700]: Produced 3 at buffer[7]
***ERR*** Producer ignoring new item with id=110
Consumer[1104366]: Consumed 30 at buffer[5]
Producer[1106700]: Produced 47 at buffer[8]
Consumer[1104366]: Consumed 39 at buffer[6]
Producer[1106700]: Produced 55 at buffer[9]
Consumer[1104366]: Consumed 3 at buffer[7]
Producer[1106700]: Produced 76 at buffer[0]
Consumer[1104366]: Consumed 47 at buffer[8]
Producer[1106700]: Produced 72 at buffer[1]
Consumer[1104366]: Consumed 55 at buffer[9]
Producer[1106700]: Produced 69 at buffer[2]
Consumer[1104366]: Consumed 76 at buffer[0]
Producer[1106700]: Produced 87 at buffer[3]
Consumer[1104366]: Consumed 72 at buffer[1]
Producer[1106700]: Produced 17 at buffer[4]
Consumer[1104366]: Consumed 69 at buffer[2]
Producer[1106700]: Produced 102 at buffer[5]
Consumer[1104366]: Consumed 87 at buffer[3]
Producer[1106700]: Produced 90 at buffer[6]
Consumer[1104366]: Consumed 17 at buffer[4]
Producer[1106700]: Produced 77 at buffer[7]
Consumer[1104366]: Consumed 102 at buffer[5]
Producer[1106700]: Produced 91 at buffer[8]
Consumer[1104366]: Consumed 90 at buffer[6]
Producer[1106700]: Produced 40 at buffer[9]
Consumer[1104366]: Consumed 77 at buffer[7]
Producer[1106700]: Produced 29 at buffer[0]
Consumer[1104366]: Consumed 91 at buffer[8]
Producer[1106700]: Produced 54 at buffer[1]
Consumer[1104366]: Consumed 40 at buffer[9]
Producer[1106700]: Produced 50 at buffer[2]
Consumer[1104366]: Consumed 29 at buffer[0]
Producer[1106700]: Produced 53 at buffer[3]
Consumer[1104366]: Consumed 54 at buffer[1]
Producer[1106700]: Produced 62 at buffer[4]
Consumer[1104366]: Consumed 50 at buffer[2]
Producer[1106700]: Produced 18 at buffer[5]
Consumer[1104366]: Consumed 53 at buffer[3]
Producer[1106700]: Produced 74 at buffer[6]
Consumer[1104366]: Consumed 62 at buffer[4]
Producer[1106700]: Produced 81 at buffer[7]
Consumer[1104366]: Consumed 18 at buffer[5]
Producer[1106700]: Produced 92 at buffer[8]
Consumer[1104366]: Consumed 74 at buffer[6]
Producer[1106700]: Produced 12 at buffer[9]
Consumer[1104366]: Consumed 81 at buffer[7]
Producer[1106700]: Produced 104 at buffer[0]
Consumer[1104366]: Consumed 92 at buffer[8]
Producer[1106700]: Produced 1 at buffer[1]
Consumer[1104366]: Consumed 12 at buffer[9]
Producer[1106700]: Produced 45 at buffer[2]
Consumer[1104366]: Consumed 104 at buffer[0]
Producer[1106700]: Produced 82 at buffer[3]
Consumer[1104366]: Consumed 1 at buffer[1]
Producer[1106700]: Produced 108 at buffer[4]
Consumer[1104366]: Consumed 45 at buffer[2]
Producer[1106700]: Produced 15 at buffer[5]
Consumer[1104366]: Consumed 82 at buffer[3]
Producer[1106700]: Produced 37 at buffer[6]
Consumer[1104366]: Consumed 108 at buffer[4]
Producer[1106700]: Produced 5 at buffer[7]
Consumer[1104366]: Consumed 15 at buffer[5]
Producer[1106700]: Produced 86 at buffer[8]
Consumer[1104366]: Consumed 37 at buffer[6]
Producer[1106700]: Produced 11 at buffer[9]
Consumer[1104366]: Consumed 5 at buffer[7]
Producer[1106700]: Produced 32 at buffer[0]
Consumer[1104366]: Consumed 86 at buffer[8]
Producer[1106700]: Produced 60 at buffer[1]
Consumer[1104366]: Consumed 11 at buffer[9]
Producer[1106700]: Produced 97 at buffer[2]
Consumer[1104366]: Consumed 32 at buffer[0]
Producer[1106700]: Produced 6 at buffer[3]
Consumer[1104366]: Consumed 60 at buffer[1]
Producer[1106700]: Produced 35 at buffer[4]
Consumer[1104366]: Consumed 97 at buffer[2]
Producer[1106700]: Produced 85 at buffer[5]
Consumer[1104366]: Consumed 6 at buffer[3]
Producer[1106700]: Produced 41 at buffer[6]
Consumer[1104366]: Consumed 35 at buffer[4]
Producer[1106700]: Produced 99 at buffer[7]
Consumer[1104366]: Consumed 85 at buffer[5]
Producer[1106700]: Produced 34 at buffer[8]
Consumer[1104366]: Consumed 41 at buffer[6]
Producer[1106700]: Produced 59 at buffer[9]
Consumer[1104366]: Consumed 99 at buffer[7]
Producer[1106700]: Produced 78 at buffer[0]
Consumer[1104366]: Consumed 34 at buffer[8]
Producer[1106700]: Produced 61 at buffer[1]
Consumer[1104366]: Consumed 59 at buffer[9]
Producer[1106700]: Produced 101 at buffer[2]
Consumer[1104366]: Consumed 78 at buffer[0]
Producer[1106700]: Produced 10 at buffer[3]
Consumer[1104366]: Consumed 61 at buffer[1]
Producer[1106700]: Produced 98 at buffer[4]
Consumer[1104366]: Consumed 101 at buffer[2]
Producer[1106700]: Produced 27 at buffer[5]
***ERR*** Producer ignoring new item with id=111
Consumer[1104366]: Consumed 10 at buffer[3]
Producer[1106700]: Produced 42 at buffer[6]
Consumer[1104366]: Consumed 98 at buffer[4]
Producer[1106700]: Produced 46 at buffer[7]
Consumer[1104366]: Consumed 27 at buffer[5]
Producer[1106700]: Produced 80 at buffer[8]
Consumer[1104366]: Consumed 42 at buffer[6]
Producer[1106700]: Produced 19 at buffer[9]
Consumer[1104366]: Consumed 46 at buffer[7]
Producer[1106700]: Produced 48 at buffer[0]
Consumer[1104366]: Consumed 80 at buffer[8]
Producer[1106700]: Produced 89 at buffer[1]
Consumer[1104366]: Consumed 19 at buffer[9]
Producer[1106700]: Produced 8 at buffer[2]
Consumer[1104366]: Consumed 48 at buffer[0]
Producer[1106700]: Produced 28 at buffer[3]
Consumer[1104366]: Consumed 89 at buffer[1]
Producer[1106700]: Produced 24 at buffer[4]
Consumer[1104366]: Consumed 8 at buffer[2]
Producer[1106700]: Produced 36 at buffer[5]
Consumer[1104366]: Consumed 28 at buffer[3]
Producer[1106700]: Produced 13 at buffer[6]
Consumer[1104366]: Consumed 24 at buffer[4]
Producer[1106700]: Produced 52 at buffer[7]
Consumer[1104366]: Consumed 36 at buffer[5]
Producer[1106700]: Produced 57 at buffer[8]
Consumer[1104366]: Consumed 13 at buffer[6]
Producer[1106700]: Produced 51 at buffer[9]
Consumer[1104366]: Consumed 52 at buffer[7]
Producer[1106700]: Produced 31 at buffer[0]
Consumer[1104366]: Consumed 57 at buffer[8]
Producer[1106700]: Produced 44 at buffer[1]
Consumer[1104366]: Consumed 51 at buffer[9]
Producer[1106700]: Produced 84 at buffer[2]
Consumer[1104366]: Consumed 31 at buffer[0]
Producer[1106700]: Produced 64 at buffer[3]
Consumer[1104366]: Consumed 44 at buffer[1]
Producer[1106700]: Produced 67 at buffer[4]
Consumer[1104366]: Consumed 84 at buffer[2]
Producer[1106700]: Produced 93 at buffer[5]
Consumer[1104366]: Consumed 64 at buffer[3]
Producer[1106700]: Produced 96 at buffer[6]
Consumer[1104366]: Consumed 67 at buffer[4]
Producer[1106700]: Produced 4 at buffer[7]
Consumer[1104366]: Consumed 93 at buffer[5]
Producer[1106700]: Produced 20 at buffer[8]
Consumer[1104366]: Consumed 96 at buffer[6]
Producer[1106700]: Produced 105 at buffer[9]
Consumer[1104366]: Consumed 4 at buffer[7]
Producer[1106700]: Produced 49 at buffer[0]
Consumer[1104366]: Consumed 20 at buffer[8]
Producer[1106700]: Produced 95 at buffer[1]
Consumer[1104366]: Consumed 105 at buffer[9]
Producer[1106700]: Produced 68 at buffer[2]
Consumer[1104366]: Consumed 49 at buffer[0]
Producer[1106700]: Produced 38 at buffer[3]
Consumer[1104366]: Consumed 95 at buffer[1]
Producer[1106700]: Produced 43 at buffer[4]
Consumer[1104366]: Consumed 68 at buffer[2]
Producer[1106700]: Produced 66 at buffer[5]
Consumer[1104366]: Consumed 38 at buffer[3]
Producer[1106700]: Produced 7 at buffer[6]
Consumer[1104366]: Consumed 43 at buffer[4]
Producer[1106700]: Produced 94 at buffer[7]
Consumer[1104366]: Consumed 66 at buffer[5]
Producer[1106700]: Produced 33 at buffer[8]
Consumer[1104366]: Consumed 7 at buffer[6]
Producer[1106700]: Produced 73 at buffer[9]
Consumer[1104366]: Consumed 94 at buffer[7]
Producer[1106700]: Produced 70 at buffer[0]
Consumer[1104366]: Consumed 33 at buffer[8]
Producer[1106700]: Produced 103 at buffer[1]
Consumer[1104366]: Consumed 73 at buffer[9]
Producer[1106700]: Produced 14 at buffer[2]
Consumer[1104366]: Consumed 70 at buffer[0]
Producer[1106700]: Produced 79 at buffer[3]
Consumer[1104366]: Consumed 103 at buffer[1]
Producer[1106700]: Produced 25 at buffer[4]
Consumer[1104366]: Consumed 14 at buffer[2]
Producer[1106700]: Produced 100 at buffer[5]
Consumer[1104366]: Consumed 79 at buffer[3]
Producer[1106700]: Produced 106 at buffer[6]
Consumer[1104366]: Consumed 25 at buffer[4]
Producer[1106700]: Produced 16 at buffer[7]
Consumer[1104366]: Consumed 100 at buffer[5]
Producer[1106700]: Produced 21 at buffer[8]
Consumer[1104366]: Consumed 106 at buffer[6]
Producer[1106700]: Produced 26 at buffer[9]
Consumer[1104366]: Consumed 16 at buffer[7]
Producer[1106700]: Produced 2 at buffer[0]
Consumer[1104366]: Consumed 21 at buffer[8]
Producer[1106700]: Produced 3 at buffer[1]
Consumer[1104366]: Consumed 26 at buffer[9]
Producer[1106700]: Produced 30 at buffer[2]
Consumer[1104366]: Consumed 2 at buffer[0]
Producer[1106700]: Produced 39 at buffer[3]
Consumer[1104366]: Consumed 3 at buffer[1]
Producer[1106700]: Produced 47 at buffer[4]
Consumer[1104366]: Consumed 30 at buffer[2]
Producer[1106700]: Produced 55 at buffer[5]
Consumer[1104366]: Consumed 39 at buffer[3]
Producer[1106700]: Produced 58 at buffer[6]
Consumer[1104366]: Consumed 47 at buffer[4]
Producer[1106700]: Produced 65 at buffer[7]
Consumer[1104366]: Consumed 55 at buffer[5]
Producer[1106700]: Produced 71 at buffer[8]
Consumer[1104366]: Consumed 58 at buffer[6]
Producer[1106700]: Produced 75 at buffer[9]
Consumer[1104366]: Consumed 65 at buffer[7]
Producer[1106700]: Produced 83 at buffer[0]
Consumer[1104366]: Consumed 71 at buffer[8]
Producer[1106700]: Produced 88 at buffer[1]
Consumer[1104366]: Consumed 75 at buffer[9]
Producer[1106700]: Produced 9 at buffer[2]
Consumer[1104366]: Consumed 83 at buffer[0]
Producer[1106700]: Produced -999999999 at buffer[3]
Consumer[1104366]: Consumed 88 at buffer[1]
Producer[1106700]: exiting.....
Consumer[1104366]: Consumed 9 at buffer[2]
Consumer[1104366]: Consumed -999999999 at buffer[3]
Consumer[1104366] exiting....
Multithreaded Programming :: Improving Performance through Threads
(http://vergil.chemistry.gatech.edu/resources/programming/threads.htm)
Most code written today is sequential. What do we mean by the term
sequential or serialized? Simply put, code is executed one instruction after
the next in a monolithic fashion, with no regard to the many possible
resources available to the program. Overall performance can be serverely
degraded if the program performs a blocking call.
Why is it that most programs are sequential? One guess could be the relative
dominance of uniprocessing machines available to programmers. Multithreading
a program on a uniprocessor machine in most cases does not yield enough
performance gains to merit days, weeks, or months worth of work to thread
code. Another guess is that most of us think in a sequential manner.
Parallelizing our thoughts does not come naturally nor is it an easy task.
However, times have changed and many papers have been written on
multithreading. Some advocate the use of threads, while others do not. With
the increasing popularity of Symmetric-Multiprocessing machines, programming
multithreaded code is a skill worth learning.
We will dive into the world of threads with some a little bit of "theory"
first. We will examine thread synchronization primitives and then a tutorial
on how to use POSIX pthreads. Finally, we will finish off with thread
performance and a brief overview of multiprocess programming.
What is a thread?
Part I :: Definition
Isn't that something you put through an eye of a sewing needle? Yes.
How does it relate to programming then?
Think of sewing needles as the CPUs (or LWPs) and the threads in a
program as the fiber. If you had two needles but only one thread, it would
take longer to finish the job than if you split the thread into two and used
both needles at the same time. Taking this analogy a little further, if one
needle had to sew on a button (blocking I/O), the other needle could continue
doing other useful work even if the other needle took 4 hours to sew on a
single button. If you only used one needle, you would be ~4 hours behind!
Now that we have pictures of dancing needles in our heads, lets move onto
something more concrete. A thread is a sequence of instructions that can be
executed in parallel with other threads [wikipedia.com]. They are not
processes, but rather lightweight threads of execution. This seems like a
recursive definition but it makes sense. Threads of a program are not full-
blown processes, but are smaller portions of the process running concurrently
(or in parallel). Hence, the term lightweight is used.
Part II :: Operating System Support
You cannot expect a multithreaded program to run on a kernel that does
not support threads. Fortunately most modern Operating Systems support
threads, either with their own thread library or through POSIX pthreads. Sun
Solaris, FreeBSD, Linux, AIX, HP-UX, IRIX, and Windows NT, just to name a
few, support multithreaded programs. However, each Operating System uses a
different technique to support threads.
Before we can dive into the details of how threads are supported, we need to
get familiarized with a few terms.
* Lightweight Process (LWP) can be thought of as a virtual CPU where the
number of LWPs is usually greater than the number of CPUs in the system.
Thread libraries communicate with LWPs to schedule threads. LWPs are
also sometimes referred to as kernel threads.
* X-to-Y model. The mapping between LWPs and Threads.
* Contention Scope is how threads compete for system resources (e.g.
scheduling)
* Bound threads have system-wide contention scope, in other words, these
threads contend with other processes on the entire system (and thus are
scheduled by the kernel)
* Unbound threads have process contention scope, in other words, these
threads are scheduled by the library onto available LWPs
Solaris uses the many-to-many model. CPUs are mapped to any number of LWPs
which are then mapped to any number of threads. The kernel schedules the LWPs
for slices of CPU time.
Linux uses the one-to-one model. Each thread is mapped to a single LWP. Why
is this approach favored in Linux over the many-to-many model? Linux LWPs are
really lightweight and thus LWP creation is not as expensive as it is in
Solaris. Another bonus to make your program multithreaded rather than
multiprocess in Linux is that the scheduler (2.4) gives a 1 point boost to
"processes" scheduled which are in the same thread family as the currently
running process.
Moreover, creating bound or unbound threads can greatly impact performance of
your multithreaded program. There is no general rule when it comes to using
either one. Each scenario demands a thorough analysis to select the right
thread type for the job.
Part III :: Other Terms
* Thread-safe means that the program protects shared data, possibly
through the use of mutual exclusion
* Reentrant code means that a program can have more than one thread
executing concurrently
* Async-safe means that a function is reentrant while handling a signal
(e.g. can be called from a signal handler)
* Concurrency vs. Parallelism - They are not the same! Parallelism is a
subset of Concurrency. Parallelism implies simultaneous running of code
(which is impossible on uniprocessor machines) while Concurrency implies
that many tasks can run in any order and possibly in parallel.
Part IV :: Threads Rule!
Yes, threads are great... for the right tasks! Don't waste your time
multithreading a program that isn't worth multithreading. Sometimes
event-based programming is the better route, or just plain, sequential, and
no-frills code can do the job just right.
Wow, you wasted an entire section on that? Yep, I can't stress enough how
important picking the right tool for the job is.
Parent process exiting.