So, I know you guys love to go into details and I promise to provide more as much as they seem to be really relevant, but for my case what you should bear in mind is the following:
I have P threads that are producers
I have C threads that are consumers
I have a buffer of N elements
I want to finish the program after it consumes M elements.
How can I do that?
Below you have my main function
int main(int argc, char *argv[]) {
int i;
/* Verify the correct number of arguments were passed in */
if(argc != 4) {
fprintf(stderr, "USAGE:./main.out <INT> <INT> <INT>\n");
}
int mainSleepTime = atoi(argv[1]); /* Time in seconds for main to sleep */
int numProd = atoi(argv[2]); /* Number of producer threads */int numCons = atoi(argv[3]); /* Number of consumer threads */
/* Initialize the app */
initializeData();
/* Create the producer threads */
for(i = 0; i < numProd; i++) {
/* Create the thread */
pthread_create(&tid,&attr,producer,NULL);
}
/* Create the consumer threads */
for(i = 0; i < numCons; i++) {
/* Create the thread */
pthread_create(&tid,&attr,consumer,NULL);
}
/* Sleep for the specified amount of time in milliseconds */
/* Exit the program */
sleep(mainSleepTime);
printf("Exit the program\n");
exit(0);
}
Right now the program computes during the sleeping time. But I want to measure the different times it will take to do M executions if I vary N. InitializeData is just a function to work on the buffer. I believe this trick should be solved in the main. Can anybody help me?
Please see the following as an example:
struct
{
int nProducednProduced;
pthread_mutex_t producerMutex;
int nConsumed;
pthread_mutex_t consuerMutex;
} monitor;
The monitor is a global object visible from all the threads and initalized by main() before thread creation.
The producers should increment monitor.nProduced and stop/exit when it reaches M.
The consumers increment monitor.nConsumed and stop/exit when it reaches M.
main() either monitors monitor.nConsumed and exit when it reaches M or simply waits until all the producers and consumers finished.
Just write a global function accessible by both producer and consumer and guard this function's entry and exit points with mutex in order to safe guard this critical section from multiple simultaneous access. Now inside this function you can use a counters to check how many items are produced by producer and how many items are consumed by consumer. This can happen if you call this function from producer code at a point before producer produces item. Similarly you can call this function after consumer consumes a item.
Related
I want each thread to synchronize at the end of every loop. I have a condition variable at the end, which sends the thread to sleep if the other threads have not reached the pseudo-barrier at the end of the thread. I keep getting a deadlock. Can you help me spot my mistake?
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>
pthread_cond_t continue_cond;
pthread_mutex_t continue_mut;
pthread_mutex_t waiting_threads_mut;
int num_waiting_threads = 0;
pthread_mutex_t working_threads_mut;
int num_working_threads = 0;
int AllThreadsHere() {
pthread_mutex_lock(&waiting_threads_mut);
pthread_mutex_lock(&working_threads_mut);
//printf("%d: %d\n", num_waiting_threads, num_working_threads);
int res = (num_waiting_threads == num_working_threads) ? 1 : 0;
pthread_mutex_unlock(&working_threads_mut);
pthread_mutex_unlock(&waiting_threads_mut);
return res;
}
// used to
void WorkerProcess(int* thread_id) {
int to_process_indices = 1000;
while (to_process_indices > 0) {
// do computation here
to_process_indices -= *(thread_id + 1);
//
pthread_cond_broadcast(&continue_cond);
// increment number of waiting threads
pthread_mutex_lock(&waiting_threads_mut);
++num_waiting_threads;
pthread_mutex_unlock(&waiting_threads_mut);
// this mutex is necessary so as to make the process of sleeping, and decrementing the number of waiting threads atomic.
// note that if a thread wakes up from sleeping, then this mutex is locked again, meaning, the process of decrementing the number of working threads cannot occur without it unlocking.
// this is very important, as an incoming thread may otherwise just finish its chunk
pthread_mutex_lock(&continue_mut);
while (AllThreadsHere() == 0) {
//printf("Thread %d sleeping\n", args->thread_id);
// waits for signal from incoming threads.
pthread_cond_wait(&continue_cond, &continue_mut);
//printf("Thread %d woken\n", args->thread_id);
}
pthread_mutex_unlock(&continue_mut);
// need to decrease the number of waiting threads.
pthread_mutex_lock(&waiting_threads_mut);
--num_waiting_threads;
pthread_mutex_unlock(&waiting_threads_mut);
}
pthread_mutex_lock(&continue_mut);
pthread_mutex_lock(&working_threads_mut);
--num_working_threads;
pthread_cond_broadcast(&continue_cond);
pthread_mutex_unlock(&working_threads_mut);
pthread_mutex_unlock(&continue_mut);
}
and here is my int main which simply initialises the mutexes, pthreads, and joins the launched threads at the end.
int main() {
const unsigned int NUM_THREADS = 3;
const double PRECISION = 0.1;
// make the space for worker threads.
pthread_t* worker_threads = malloc(NUM_THREADS * sizeof(pthread_t));
int* worker_ids = malloc(sizeof(int) * NUM_THREADS);
pthread_cond_init(&continue_cond, NULL);
pthread_mutex_init(&waiting_threads_mut, NULL);
pthread_mutex_init(&working_threads_mut, NULL);
pthread_mutex_init(&continue_mut, NULL);
for (unsigned int k = 0; k < NUM_THREADS; ++k) {
worker_ids[k] = k;
pthread_create(worker_threads + k, NULL, WorkerProcess, (void*)(worker_ids + k));
}
for (unsigned int k = 0; k < NUM_THREADS; ++k) {
pthread_join(worker_threads[k], NULL);
}
}
Throwing code together and moving it around until it seems to work is not software development, it is gambling.
Instead, consider the behaviour you want from your barrier.
In my opinion, the barrier needs a condition variable that threads can wait on, until the number of threads reaches the set number of threads, at which point all waiting threads are woken up and released to proceed.
If the iteration is fast enough, then it is possible that the first-released thread arrives again at the barrier before all threads waiting in the barrier have released. So, we need a second condition variable for such incoming threads, plus at least one flag to indicate when the barrier is still being released.
Let's create a structure to describe such a barrier. Because linux lets us initialize mutexes and condition variables statically, we'll also define a static initializer.
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
enum {
BARRIER_RELEASED = (1 << 0),
};
typedef struct {
pthread_mutex_t lock;
// Threads that arrive at the barrier before it has released
// all threads blocked in it, will wait on 'incoming'.
pthread_cond_t incoming;
// Threads blocked on the barrier wait on 'waiting'.
pthread_cond_t waiting;
// Number of threads in the barrier, or release trigger limit.
int limit;
// Number of threads blocked on the barrier (waiting on 'waiting').
int count;
// Barrier state flags, one bit per flag. See BARRIER_ flag enums.
int state;
} barrier;
#define BARRIER_INITIALIZER(limit_) \
{ .lock = PTHREAD_MUTEX_INITIALIZER, \
.incoming = PTHREAD_COND_INITIALIZER, \
.waiting = PTHREAD_COND_INITIALIZER, \
.limit = limit_, \
.count = 0, \
.state = 0 }
Since we do not necessarily know the number of threads participating in the barrier at compile time, let's also define an init function:
void barrier_init(barrier *b, int limit)
{
if (!b) {
fprintf(stderr, "barrier_init(): No barrier (NULL) specified.\n");
exit(EXIT_FAILURE);
}
if (limit < 0) {
fprintf(stderr, "barrier_init(): Negative limit (%d) specified.\n", limit);
exit(EXIT_FAILURE);
}
pthread_mutex_init(&(b->lock), NULL); // Cannot fail in Linux
pthread_cond_init(&(b->incoming), NULL); // Cannot fail in Linux
pthread_cond_init(&(b->waiting), NULL); // Cannot fail in Linux
b->limit = limit;
b->count = 0;
b->state = 0;
}
In both the initializer and the init function, the limit is the (expected) number of threads participating in the barrier.
(If we have each thread register itself in the barrier, then the first thread might iterate several times by itself while the barrier thread count is 1, before any other threads have a chance of registering themselves also.)
Whenever a thread no longer wants to participate in a barrier, it needs to "leave", so that the other threads waiting in the barrier won't wait there forever for the missing thread.
// When a thread no longer participates in a barrier, it needs to leave,
// so that the rest of the threads can keep gathering at the barrier without hanging.
void barrier_leave(barrier *b)
{
if (!b) {
fprintf(stderr, "barrier_leave(): No barrier (NULL) specified.\n");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&(b->lock));
b->limit--;
// Decreasing the limit may release the threads waiting on the barrier!
if (!(b->state & BARRIER_RELEASED) && b->count >= b->limit) {
b->state |= BARRIER_RELEASED;
pthread_cond_broadcast(&(b->waiting));
}
pthread_mutex_unlock(&(b->lock));
}
For completeness, we can define a function so that if one does not know the limit beforehand, one can init the barrier to an impossibly large number of threads, say SIZE_MAX/2, and then re-set the limit to the actual number of threads. This way, the created threads will start normally but wait at the barrier:
void barrier_set_limit(barrier *b, int limit)
{
if (!b) {
fprintf(stderr, "barrier_set_limit(): No barrier (NULL) specified.\n");
exit(EXIT_FAILURE);
}
if (limit < 0) {
fprintf(stderr, "barrier_set_limit(): Invalid, negative limit (%d) set.\n", limit);
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&(b->lock));
b->limit = limit;
// Decreasing the limit may release the threads waiting on the barrier!
if (!(b->state & BARRIER_RELEASED) && b->count >= b->limit) {
b->state |= BARRIER_RELEASED;
pthread_cond_broadcast(&(b->waiting));
}
pthread_mutex_unlock(&(b->lock));
}
The final function left is the waiting at the barrier.
// Wait/gather at a barrier.
void barrier_wait(barrier *b)
{
if (!b) {
fprintf(stderr, "barrier_wait(): No barrier (NULL) specified.\n");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&(b->lock));
// Wait, if the barrier is being released.
if (b->state & BARRIER_RELEASED)
pthread_cond_wait(&(b->incoming), &(b->lock));
b->count++;
if (b->count >= b->limit) {
// We filled the barrier: release.
b->state |= BARRIER_RELEASED;
pthread_cond_broadcast(&(b->waiting));
} else {
// Barrier wasn't full, so we wait.
pthread_cond_wait(&(b->waiting), &(b->lock));
}
// If we are the last thread out of the barrier, we need to update
// the barrier state, and let the incoming threads advance.
b->count--;
if (b->count <= 0) {
b->state &= ~(BARRIER_RELEASED);
pthread_cond_broadcast(&(b->incoming));
}
pthread_mutex_unlock(&(b->lock));
}
Quick testing indicates the above indeed works, as long as the barrier is first initialized to the number of threads, and each thread participating in the barrier calls barrier_leave() before it exits.
In other words, you have a global, say static barrier turnstile;, and before creating say n threads, you call barrier_init(&turnstile, n);. Within the thread worker function, inside your loop, you call barrier_wait(&turnstile); to synchronize those threads. If one of those threads wants or needs to exit, it should call barrier_leave(&turnstile); first.
As you can see, the code looks nothing like yours. Everything related to a barrier is contained within the barrier structure, and all functions needed to manipulate barriers start with a barrier_ prefix, and should be straightforward to understand.
The entire process here started at thinking about "What do I need?", then implementing each need step by step. While I was writing this answer, I did need to "go back one step", because I didn't initially remember that an incoming condition queue is also needed, in case the first released thread(s) reach the same barrier again before all waiting threads in it have been released. But, because I went about it constructively, one step at a time (instead of throwing a lot of code together and then trying to see if it compiles and works), it wasn't a huge change –– even though for simplicity I rewrote the functions from scratch, only keeping the error checks.
I am working on this problem: take from the command line a letter and the name of same files, count the occurrence of the char in each file, using one thread per file, and and print the total occurrences.
This is my code:
typedef struct _CharFile{
char c;
char *fileName;
} CharFile;
pthread_mutex_t count = PTHREAD_MUTEX_INITIALIZER;
int sum = 0;
void *CountFile(void *threadarg);
int main(int argc, const char * argv[]) {
pthread_t threads[argc-2];
int chck, t;
CharFile cf;
for ( t=0 ; t<argc-2 ; t++ ){
cf.c = argv[1][0];
cf.fileName = (char *)argv[t + 2];
chck = pthread_create(&threads[t], NULL, CountFile, (void *) &cf);
if (chck){
printf("ERROR; return code from pthread_create() is %d\n", chck);
exit(-1);
}
}
printf("%lld occurrences of the letter %c in %lld threads\n", (long long)sum, argv[1][0], (long long)argc-2);
return 0;
}
void *CountFile(void *threadarg){
FILE *in;
CharFile *cf;
char c;
int counter = 0;
cf = (CharFile *) threadarg;
in = fopen(cf->fileName, "r");
if (in == NULL){
perror("Error opening the file!\n");
pthread_exit(NULL);
}
while (fscanf(in, "%c", &c) != EOF){
if(c == cf->c){
counter += 1;
}
}
fclose(in);
pthread_mutex_lock(&count);
sum += counter;
pthread_mutex_unlock(&count);
pthread_exit(NULL);
}
I don't get any error in the file opening or in the thread creations, but my output is always 0 as total occurrences. I also tried to print the counter in the threads and I got every time the same numbers in all the threads, even if my input files are different. Am I using the mutex wrongly or is there something else wrong?
This is one of my outputs:
61 occurrences of e in this thread
0 occurrences of the letter e in 3 threads
61 occurrences of e in this thread
61 occurrences of e in this thread
Program ended with exit code: 9
There are several threading issues at play here.
1) The main thread will continue asynchronously to the newly spawned threads. Given the code, it is very likely that the main thread will complete and exit before the CountFile threads have completed. On Linux, when the main thread returns, the C runtime will perform a exit_group system call which will terminate all threads.
You'll need to add some check to ensure the CountFile threads have finished the relevant section of work. In this example, look at using pthread_join() in the main thread.
2) The 'cf' storage in the main thread is a stack local variable, which is passed by pointer to each thread. However, since it is the same storage, several types of failures could occur. a) the workunit may be updated by the main thread while a worker thread is accessing it. b) the same workunit is sent to multiple/all threads.
You could solve this several ways: 'cf' could be an array of CharFile for each thread. Or 'cf' could be dynamically allocated for each thread. The former is a bit more performance and memory efficient, but the later might be better structurally. Particularly that main thread is giving addresses in its local stack space to another thread.
3) Once item #1 is addressed and the threads exited before the main thread printf, the mutex usage would be ok. But it might be better to put pthread_mutex_locks around the main thread access of 'sum' anyway. It may not be necessary given this code, but future code refactors might change that.
For example I want to create 5 threads and print them. How do I make the fourth one execute before the second one? I tried locking it with a mutex, but I don't know how to make only the second one locked, so it gives me segmentation fault.
Normally, you define the order of operations, not the threads that do those operations. It may sound like a trivial distinction, but when you start implementing it, you'll see it makes for a major difference. It is also more efficient approach, because you don't think of the number of threads you need, but the number of operations or tasks to be done, and how many of them can be done in parallel, and how they might need to be ordered or sequenced.
For learning purposes, however, it might make sense to look at ordering threads instead.
The OP passes a pointer to a string for each worker thread function. That works, but is slightly odd; typically you pass an integer identifier instead:
#include <stdlib.h>
#include <inttypes.h>
#include <pthread.h>
#define ID_TO_POINTER(id) ((void *)((intptr_t)(id)))
#define POINTER_TO_ID(ptr) ((intptr_t)(ptr))
The conversion of the ID type -- which I assume to be a signed integer above, typically either an int or a long -- to a pointer is done via two casts. The first cast is to intptr_t type defined in <stdint.h> (which gets automatically included when you include <inttypes.h>), which is a signed integer type that can hold the value of any void pointer; the second cast is to a void pointer. The intermediate cast avoids a warning in case your ID is of an integer type that cannot be converted to/from a void pointer without potential loss of information (usually described in the warning as "of different size").
The simplest method of ordering POSIX threads, that is not that dissimilar to ordering operations or tasks or jobs, is to use a single mutex as a lock to protect the ID of the thread that should run next, and a related condition variable for threads to wait on, until their ID appears.
The one problem left, is to how to define the order. Typically, you'd simply increment or decrement the ID value -- decrementing means the threads would run in descending order of ID value, but the ID value of -1 (assuming you number your threads from 0 onwards) would always mean "all done", regardless of the number of threads used:
static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t worker_wait = PTHREAD_COND_INITIALIZER;
static int worker_id = /* number of threads - 1 */;
void *worker(void *dataptr)
{
const int id = POINTER_TO_ID(dataptr);
pthread_mutex_lock(&worker_lock);
while (worker_id >= 0) {
if (worker_id == id) {
/* Do the work! */
printf("Worker %d running.\n", id);
fflush(stdout);
/* Choose next worker */
worker_id--;
pthread_cond_broadcast(&worker_wait);
}
/* Wait for someone else to broadcast on the condition. */
pthread_cond_wait(&worker_wait, &worker_lock);
}
/* All done; worker_id became negative.
We still hold the mutex; release it. */
pthread_mutex_unlock(&worker_lock);
return NULL;
}
Note that I didn't let the worker exit immediately after its task is done; this is because I wanted to expand the example a bit: let's say you want to define the order of operations in an array:
static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t worker_wait = PTHREAD_COND_INITIALIZER;
static int worker_order[] = { 0, 1, 2, 3, 4, 2, 3, 1, 4, -1 };
static int *worker_idptr = worker_order;
void *worker(void *dataptr)
{
const int id = POINTER_TO_ID(dataptr);
pthread_mutex_lock(&worker_lock);
while (*worker_idptr >= 0) {
if (*worker_idptr == id) {
/* Do the work! */
printf("Worker %d running.\n", id);
fflush(stdout);
/* Choose next worker */
worker_idptr++;
pthread_cond_broadcast(&worker_wait);
}
/* Wait for someone else to broadcast on the condition. */
pthread_cond_wait(&worker_wait, &worker_lock);
}
/* All done; worker_id became negative.
We still hold the mutex; release it. */
pthread_mutex_unlock(&worker_lock);
return NULL;
}
See how little changed?
Let's consider a third case: a separate thread, say the main thread, decides which thread will run next. In this case, we need two condition variables: one for the workers to wait on, and the other for the main thread to wait on.
static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t worker_wait = PTHREAD_COND_INITIALIZER;
static pthread_cond_t worker_done = PTHREAD_COND_INITIALIZER;
static int worker_id = 0;
void *worker(void *dataptr)
{
const int id = POINTER_TO_ID(dataptr);
pthread_mutex_lock(&worker_lock);
while (worker_id >= 0) {
if (worker_id == id) {
/* Do the work! */
printf("Worker %d running.\n", id);
fflush(stdout);
/* Notify we are done. Since there is only
one thread waiting on the _done condition,
we can use _signal instead of _broadcast. */
pthread_cond_signal(&worker_done);
}
/* Wait for a change in the worker_id. */
pthread_cond_wait(&worker_wait, &worker_lock);
}
/* All done; worker_id became negative.
We still hold the mutex; release it. */
pthread_mutex_unlock(&worker_lock);
return NULL;
}
The thread that decides which worker should run first should hold the worker_lock mutex when the worker threads are created, then wait on the worker_done condition variable. When the first worker completes its task, it will signal on the worker_cone condition variable, and wait on the worker_wait condition variable. The decider thread should then change the worker_id to the next ID that should run, and broadcast on the worker_wait condition variable. This continues, until the decider thread sets worker_id to a negative value. For example:
int threads; /* number of threads to create */
pthread_t *ptids; /* already allocated for that many */
pthread_attr_t attrs;
int i, result;
/* Simple POSIX threads will work with 65536 bytes of stack
on all architectures -- actually, even half that. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 65536);
/* Hold the worker_lock. */
pthread_mutex_lock(&worker_lock);
/* Create 'threads' threads. */
for (i = 0; i < threads; i++) {
result = pthread_create(&(ptids[i]), &attrs, worker, ID_TO_POINTER(i));
if (result) {
fprintf(stderr, "Cannot create worker threads: %s.\n", strerror(result));
exit(EXIT_FAILURE);
}
}
/* Thread attributes are no longer needed. */
pthread_attr_destroy(&attrs);
while (1) {
/*
TODO: Set worker_id to a new value, or
break when done.
*/
/* Wake that worker */
pthread_cond_broadcast(&worker_wait);
/* Wait for that worker to complete */
pthread_cond_wait(&worker_done, &worker_lock);
}
/* Tell workers to exit */
worker_id = -1;
pthread_cond_broadcast(&worker_wait);
/* and reap the workers */
for (i = 0; i < threads; i++)
pthread_join(ptids[i], NULL);
There is a very important detail in all of the above examples, that may be hard to understand without a lot of practice: the way how mutexes and condition variables interact (if paired via pthread_cond_wait()).
When a thread calls pthread_cond_wait(), it will atomically release the specified mutex, and wait for new signals/broadcasts on the condition variable. "Atomic" means that there is no time inbetween the two; nothing can occur in between. The call returns when a signal or broadcast is received -- the difference is that a signal goes to only one, a random waiter; whereas a broadcast reaches all threads waiting on the condition variable --, and the thread acquires the lock. You can think of this as if the signal/broadcast first wakes up the thread, but the pthread_cond_wait() will only return when it re-acquires the mutex.
This behaviour is implicitly used in all of the examples above. In particular, you'll notice that the pthread_cond_signal()/pthread_cond_broadcast() is always done while holding the worker_lock mutex; this ensures that the other thread or threads wake up and get to act only after the worker_lock mutex is unlocked -- either explicitly, or by the holding thread waiting on a condition variable.
I thought I might draw a directed graph (using Graphviz) about the order of events and actions, but this "answer" is already too long. I do suggest you do it yourself -- perhaps on paper? -- as that kind of visualization has been very useful for myself when I was learning about all this stuff.
I do feel quite uncomfortable about the above scheme, I must admit. At any one time, only one thread is running, and that is basically wrong: any job where tasks should be done in a specific order, should only require one thread.
However, I showed the above examples in order for you (not just OP, but any C programmer interested in POSIX threads) to get more comfortable about how to use mutexes and condition variables.
I have a problem with multithreading, since I'm new to this topic. Code below is code I've been given from my University. It was in few versions, and I understood most of them. But I don't really understand the nready.nready variable and all this thread condition. Can anyone describe how those two work here? And why can't I just synchronise work of threads via mutex?
#include "unpipc.h"
#define MAXNITEMS 1000000
#define MAXNTHREADS 100
/* globals shared by threads */
int nitems; /* read-only by producer and consumer */
int buff[MAXNITEMS];
struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int nput;
int nval;
int nready;
} nready = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER };
void *produce(void *), *consume(void *);
/* include main */
int
main(int argc, char **argv)
{
int i, nthreads, count[MAXNTHREADS];
pthread_t tid_produce[MAXNTHREADS], tid_consume;
if (argc != 3)
err_quit("usage: prodcons5 <#items> <#threads>");
nitems = min(atoi(argv[1]), MAXNITEMS);
nthreads = min(atoi(argv[2]), MAXNTHREADS);
Set_concurrency(nthreads + 1);
/* 4create all producers and one consumer */
for (i = 0; i < nthreads; i++) {
count[i] = 0;
Pthread_create(&tid_produce[i], NULL, produce, &count[i]);
}
Pthread_create(&tid_consume, NULL, consume, NULL);
/* wait for all producers and the consumer */
for (i = 0; i < nthreads; i++) {
Pthread_join(tid_produce[i], NULL);
printf("count[%d] = %d\n", i, count[i]);
}
Pthread_join(tid_consume, NULL);
exit(0);
}
/* end main */
void *
produce(void *arg)
{
for ( ; ; ) {
Pthread_mutex_lock(&nready.mutex);
if (nready.nput >= nitems) {
Pthread_mutex_unlock(&nready.mutex);
return(NULL); /* array is full, we're done */
}
buff[nready.nput] = nready.nval;
nready.nput++;
nready.nval++;
nready.nready++;
Pthread_cond_signal(&nready.cond);
Pthread_mutex_unlock(&nready.mutex);
*((int *) arg) += 1;
}
}
/* include consume */
void *
consume(void *arg)
{
int i;
for (i = 0; i < nitems; i++) {
Pthread_mutex_lock(&nready.mutex);
while (nready.nready == 0)
Pthread_cond_wait(&nready.cond, &nready.mutex);
nready.nready--;
Pthread_mutex_unlock(&nready.mutex);
if (buff[i] != i)
printf("buff[%d] = %d\n", i, buff[i]);
}
return(NULL);
}
/* end consume */
pthread_mutex_lock(&nready.mutex);
while (nready.nready == 0)
pthread_cond_wait(&nready.cond, &nready.mutex);
nready.nready--;
pthread_mutex_unlock(&nready.mutex);
The whole point of this structure is to guarantee that the condition (nready.nready == 0) is still true when you execute the corresponding action (nready.nready--) or - if the condition is not satisfied - to wait until it is without using CPU time.
You could use a mutex only, to check that the condition is correct and to perform the corresponding action atomically. But if the condition is not satisfied, you wouldn't know what to do. Wait? Until when? Check it again? Release the mutex and re-check immediately after? That would be wasting CPU time...
pthread_cond_signal() and pthread_cond_wait() are here to solve this problem. You should check their man pages.
Briefly, what pthread_cond_wait does, is it puts the calling thread to sleep and release the mutex in an atomic way until it's signaled. So this is a blocking function. The thread can then be re-scheduled by calling signal or broadcast from a different thread. When the thread is signaled, it grabs the mutex again and exit the wait() function.
Ath this point you know that
your condition is true and
you hold the mutex.
So you can do whatever you need to do with your data.
Be careful though, you shouldn't call wait, if you're not sure that another thread will signal. This is a very common source of deadlocks.
When a thread received a signal, it's put on the list of threads that are ready to be scheduled. By the time the thread is actually executed, your condition (i.e. nread.nready == 0) may be false again. Hence the while (to recheck if the thread is waked).
"But I don't really understand the nready.nready variable"
this results from the struct instance being named 'nready' and there
is a field within the struct named 'nready'
IMO: a very poor design to have two different objects being given the same name
the nready field of the nready struct seems to be keeping track of the number of
items that have been 'produced'
1) The nready filed of struct nready is used to tack how many tasks are ready to consume, i.e., the remaining tasks in array buff. The nready.nready++; statement is only executed when producers put one new item in array buff, and the nready.nready--; is only executed when consume gets item out of buff. With is variable, programmer can always track how many tasks are there left to process.
2)
pthread_mutex_lock(&nready.mutex);
while (nready.nready == 0)
pthread_cond_wait(&nready.cond, &nready.mutex);
nready.nready--;
pthread_mutex_unlock(&nready.mutex);
The statements above are common condition variable usage. You can check
POSIX Threads Programming and Condition Variables for more about condition variables.
Why can't use mutex only? You can poll a mutex lock again and again. Obviously, it is CPU time consuming and may be hugely affect system performance. Instead, you want the consume to wait in sleep when there is no more items in buff, and to be awaken when producer puts new item in buff. The condition variable is acting as this role here. When there is no items (nready.nready==0), pthread_cond_wait() function puts the current thread into sleep, save the precious cpu time. When new items are arriving, Pthread_cond_signal() awakes the consumer.
So i'm working on this code that is a producer-consumer code. It runs completely through the program, it just never actually executes anything in the critical section because it never really wakes up from sleep! I've added print statements everywhere to try to figure out where the code is executing, and it enters both producer and consumer functions, but never enters any portion of it after the sleep() function.
Here is my main:
/* 1. Get command line arguments argv[1], argv[2], argv[3] */
/* n1=arg[2], n2=arg[3]
/* 2. Initialize buffer, mutex, semaphores, and other global vars */
/*create the mutex lock */
/* create the semaphore and initialize it to 3 */
/*creating full semaphore and initializing it to 0 */
/*** critical section ***/
/* get the default attribute */
pthread_attr_init(&attr);
/* 3. Create producer thread(s) */
while (count < n1)
{
pthread_t tid;
/* create a new thread */
pthread_create(&tid, &attr, producer, NULL);
count++;
}
printf("OUTSIDE OF PRODUCER CREATION\n");
/* 4. Create consumer thread(s) */
count = 0;
while(count < n2)
{
pthread_t tid2;
/* create a new thread */
pthread_create(&tid2, &attr, consumer, NULL);
count++;
}
printf("after the crit section \n");
/* 5. Sleep */
/* 6. Realease resources, e.g destroy mutex and semaphores */
i've included for the most part just the code that I know i'm having a problem with and the rest are comments. and here is the code for my producer. consumer is basically the same:
void *producer(void *param) {
buffer_item rand;
unsigned int *seed;
seed = (unsigned int *)malloc(sizeof(unsigned int));
*seed = 10;
while (1) {
printf("Inside producer function\n");
/* Sleep for a random period of time */
r = (rand_r(seed))/divide;
sleep(r);
printf("producer slept\n");
//wait(empty)
//wait(mutex)
printf("producer locked mutex\n");
//crit section - add item to buffer
/*Generate a random number */
/*insert random number*/
printf("producer inserted item \n");
if (resp < 0)
printf("Producer error condition\n"); //Report error condition
//signal mutex
//signal empty
}
}
so when i run a.out 4 4 4, i get this as my output:
Inside producer function
Inside producer function
Inside producer function
OUTSIDE OF PRODUCER CREATION
Inside producer function
inside consumer function
inside consumer function
inside consumer function
after the crit section
inside consumer function
I'm not sure if its normal that things look like they are running out of order... it is executing 4 times but as you can see if never hits that print statement that happens after my sleep function (for both producer and consumer)
This is homework so i'm just looking for a bit more direction on this...
From the discussion, your error was that your sleep() values were too large, one you quoted coming out to 22 minutes. Rather than division(/) to limit the values, modulo(%) will put your values within a range. I recommend %10, or some other value which will limit the sleeps to a reasonable range.