I was tasked with the question of creating the appropriate semaphores and using signal
and wait
commands accordingly to make the sort work correctly. I was given the following skeleton code
Bubble.cpp
#include <iostream>
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include "sem.h"
#define NUM_CELLS 32
using namespace std;
extern sim_semaphore create_sim_sem(int) ;
extern void wait_sem (sim_semaphore) ;
extern void signal_sem (sim_semaphore) ;
/* When debugging, we need to use this lock to get
intelligible printouts of what the various threads are
doing. Warning: Don't change how this is done.
If you insist on changing it, you need to have
thought a WHOLE lot about the consequences. */
pthread_mutex_t stdoutLock ;
/* Here declare whatever semaphores, flags, counters, and
so forth, that you want to use for synchronization. Variables
declared here will be visible to (shared by) all
the threads in the task. */
/* This is the array that will be filled and then sorted by
a group of child threads. */
int cell[NUM_CELLS] ;
/* These are global variable to represent threads created dynamically. */
pthread_t thr[NUM_CELLS] ;
/* This is included to facilitate adding random delays in the code -- as a
debugging aid. */
extern long random(void);
/* This can be changed to 1, but diagnostic output will probably
seem excessive. */
int checking = 0 ;
/* A data type - a struct with an int field to represent a thread ID. */
struct threadIdType
{
int id ;
};
/* ################################################## */
/* init */
/* ################################################## */
void init()
{
/* This code initializes special lock for screen output.
Just leave this alone. */
if ( 0!=pthread_mutex_init(&stdoutLock, NULL) )
{ cout << "MUTEX INITIALIZATION FAILURE!" << endl ;
exit(-1) ;}
/* Here insert the code you want to intialize semaphores, flags, counters,
and so forth. */
/* This initializes a random number generator */
srandom(time((time_t *) 0));
}
/* ################################################## */
/* child */
/* ################################################## */
void * child(void * idPtr)
{
int m_delay, j ;
/* This is just a change of data type for convenience.
Now 'me' is the number of the child. Children have
numbers from 1 to 31=NUM_CELLS-1. */
int me = ((threadIdType *) (idPtr))->id, temp ;
do
{
/* Put entry code below here. */
/* This next segment of code is 'critical' because TWO child threads
can access most of the cells. */
if (cell[me-1] > cell[me])
{
/* Start the swap of the cell values. */
temp = cell[me-1] ;
cell[me-1] = cell[me] ;
/* I inserted delay code here below to magnify the chances
that child threads will interfere with each other.
It's a "stress test" -- if the synchronization code
the student puts in the program is good, the delays
won't cause incorrect results. On the other hand,
if the student's code is flawed, the delay code below may
increase the likelihood that the program will output
incorrect results. Leave this here. */
m_delay = (int) random()%100 ;
for (j=0; j<m_delay; j++) sched_yield();
/* Finish up with the swap. */
cell[me] = temp ;
}
/* Put exit code below here. */
} while (true) ;
pthread_exit ((void *)0) ;
}
/* ################################################## */
/* mother */
/* ################################################## */
/* The mother spawns a given number of children and then waits
for them all to finish. */
void mother()
{
int i;
/* This is a pointer to a struct that contains an int
field - it is a convenient data type to use as the
parameter to the child function. */
threadIdType * idPtr ;
for (i = 1; i < NUM_CELLS ; i++)
{
/* Mother forks a child and detaches it - she will
not join it. In other words, she will not block,
waiting for it to exit. */
idPtr = new threadIdType ;
/* This records the current index as this child's ID */
idPtr->id = i ;
/* The call below is what actually creates the child
thread and passes a pointer to the struct 'idPtr' as
the parameter to the child function. */
if ( 0!=pthread_create(&thr[i], NULL, child, (void *) idPtr) )
{ cout << "THREAD CREATION FAILURE!" << endl ;
exit(-1) ; }
if (0!=pthread_detach(thr[i]))
{ cout << "THREAD DETACHMENT FAILURE!" << endl ;
exit(-1) ;}
}
bool sorted ;
int m_delay, j;
/* The code below can be used to check to see if the
list is sorted. However, you can't leave it just as it
is. When the mother checks the condition
cell[i-1] > cell[i]
she is accessing memory that she shares with
one or two of the child threads, and the child threads
could be writing that memory concurrently, so you need to
add entry and exit code that synchronizes the
mother with the children.
You can try to think of an algorithm different from the
one below, if you want. It's possible for
the child processes to figure out collectively
whether the list is sorted. */
do
{
/* You can insert random delays like the
one below where you want - to vary
timing. */
/*
m_delay = (int) random()%100 ;
for (j=0; j<m_delay; j++) sched_yield();
*/
/* MOTHER WALKS UP THE ARRAY, CHECKING */
sorted = true ;
/* You may need to add some entry code here. */
for (i=1; i<NUM_CELLS; i++)
{
/* You may need to add some entry code here. */
if (cell[i-1] > cell[i]) sorted = false ;
/* You may need to add some exit code here. */
}
/* You may need to add some exit code here. */
} while (!sorted) ;
/* This code prints the array - which should be sorted now. */
for (i=0; i<NUM_CELLS; i++) cout << cell[i] << endl ;
}
/* ################################################## */
/* main */
/* ################################################## */
int main()
{
/* This calls the function that performs initializations. */
init();
int idx ;
/* Read the (unsorted) input into the array */
for (idx=0; idx<NUM_CELLS; idx++) cin >> cell[idx] ;
/* Echo the (unsorted) input to the screen. */
for (idx=0; idx<NUM_CELLS; idx++) cout << cell[idx]
<< endl ;
cout << endl ;
/* Execute the mother() function, which creates the
child threads that sort the list. */
mother();
return 0 ;
}
I have worked a little with semaphores before, but I am still quite new to the concept and might need a bit more guidance.
I am required to create global semaphore variables, I've done something like this before, example:
sim_semaphore empty[i] ;
sim_semaphore full[i] ;
Could I use the same logic again? Meaning while a cell has a value (aka is full) and is being swapped, I would just use the signal/wait commands accordingly? Could I solve this using only two different semaphores?
Next I would have to initialize them inside of init()
, the last time I did this I used a for-loop, example:
for (index=0; index<numTrivets; index++) full[index] = create_sim_sem(0) ;
Is this the most efficient way to do this again?
Lastly, where it says I need entry and exit code, I thought about using Peterson's solution by initializing boolean flag
and setting flag = 1
for the first pass and back to flag = 0
when it finishes. However, I have come to believe it is not the "most correct" method of trying to solve this program. What should I do in its stead?
Thanks to all who put their time towards trying to help me! If you have any questions or clarification please do not hesitate to ask me I will do my best to make this easier to help me out.
I draw my examples from my last program which is here: Indexing into an array of semaphores
I reference Peterson's solution which can be found here: http://mathbits.com/MathBits/CompSci/Arrays/Bubble.htm
To answer my question, the solution was easier than I thought. I just needed one array of semaphores at the beginning,
sim_semaphore semArray[NUM_CELLS] ;
and then initialized as stated in my question.
for (index=0; index<NUM_CELLS; index++) semArray[index] = create_sim_sem(1) ;
The child's job just consisted of signal
and wait
and the goal is to be able to swap the data all at once without having to wait one at a time. You can do this by having the child wait on i-1
and i
before the actual swapping algorithm occurs and signal on i-1
and i
after the algorithm.
The mother's job is to essentially walk down the line of data and double check that the child did its job. You can accomplish this by creating a for-loop
before and after the for-loop
already implemented. After that all you need to do is wait
on i
and signal
on i
after. This is just one solution though, there are more sophisticated solutions, but this is probably the easiest.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.