I have a Standard Producer Consumer problem for bounded buffer.Whenever i give unequal number of producer or consumer the Program does not terminate.
I have limited the number of insertion or deletion to 50
I would like to know why the above problem occurs and ways to fix it
#include<stdio.h>
#include<pthread.h> //Header for creating Posix Threads;
#include<semaphore.h>
#include<stdlib.h>
#define MAX 20
typedef struct shared_buffer
{
int arr[20];
int i;
}buffer;
buffer b; //Fixed Length buffer shared memory
sem_t full,empty; //Counting semaphores full->no of slots filled empty ->no of slots empty
pthread_mutex_t mutex; //mutual exclusion for critcal section
int flag=1,cnt=0;
void * producer(void * arg) //Producer threads method
{
int index; //thread Id
index=(int)arg;
while(flag)
{
sem_wait(&empty); //will check if slot available and decrement empty count
pthread_mutex_lock(&mutex); //acquiring lock on process
b.arr[b.i]=rand()%100; //critcal section
printf("\n Process %d Produced :%d",index,b.arr[b.i]);
b.i++;
cnt++; //critcal section ends
pthread_mutex_unlock(&mutex); //realeasing lock
sem_post(&full); //increment full count
usleep(rand()%100); //sleep for random time
}
}
void * consumer(void * arg)
{
int index;
index=(int)arg;
while(flag)
{
sem_wait(&full); //will check if buffer is not empty
pthread_mutex_lock(&mutex);
b.i--; //critical section
printf("\n Process %d consumed :%d",index,b.arr[b.i]);
cnt++; //critical section ends
pthread_mutex_unlock(&mutex); //release lock
sem_post(&empty); //increment count of empty slots
usleep(rand()%100);
}
}
int main(int argc,char * argv[])
{
pthread_t Pid,Cid;
int P,C,index,size;
b.i=0;
if(argc<4)
{
printf("Error.Usage : ./filename.out <no of producer> <no of consumer> <Buffer Size(<=15)> \n");
exit(0);
}
P=atoi(argv[1]);
C=atoi(argv[2]);
size=atoi(argv[3]);
sem_init(&full,0,0); //number of slots filled is 0
sem_init(&empty,0,size); //number of empty slots is buffer size
pthread_mutex_init(&mutex,NULL);
for (index=0;index<C;index++) //creating C number of consumer
{
pthread_create(&Cid,NULL,consumer,index);
}
for (index=0;index<P;index++) //creating P number of producer
{
pthread_create(&Pid,NULL,producer,index);
}
while(cnt<=50) //maximum 50 operations allowed
usleep(200);
flag=0;
printf("phew!Successful");
pthread_exit(NULL);
return 1;
}
Here are some clues (not full answer):
First thing that bugging me is that you are overwriting your pthread_t references in your loops. But it is OK since you don't use them afterwards.
Second thing is that you misplaced the pthread_exit: there should be at the end of the threads you created:
void * consumer_producer(void * arg)
{
// ...
while(flag)
{
// ...
}
pthread_exit(NULL);
}
I guess you have a race condition. It is possible that a thread is waiting when flag is set to 0. Other threads possibly catch that state and return, thus the waiting thread is waiting forever.
Related
I'm trying to synchronize 02 child processes that communicate using a buffer of size 5 using the semaphores "empty" and "full" only.
Process 01 executes producer() function and the second one executes consumer(), but the consumer functions does not execute at all even when the producer is blocked (empty=0).
after 5 iterations, I only get 5 insertions but no consuming ( I'm inserting 5's for now). Also I believe there is no need for a mutex since there are only 01 producer and 01 consumer right? here are the 02 functions and the main program:
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include <pthread.h>
#define MaxItems 5
#define BufferSize 5
sem_t empty;
sem_t full;
//pthread_mutex_t lock;
int in = 0;
int out = 0;
int buffer[BufferSize];
int N = 20;
void *producer()
{
while(1)
{
//pthread_mutex_lock(&lock);
sem_wait(&empty);
buffer[in] = 5; // fill the buffer with numbers
printf("\nInsert Item %d at %d\n", buffer[in],in);
int emptyvalue, fullValue;
sem_getvalue(&full, &fullValue); sem_getvalue(&empty, &emptyvalue);
printf("full is:%d, empty is:%d\n", fullValue, emptyvalue);
//pthread_mutex_unlock(&lock);
sem_post(&full); // increase the value of full, so the consumer should execute now?
in = (in+1)%BufferSize;
usleep(500000);
}
}
void *consumer()
{
printf("test: consumer"); //this consumer function does not execute
while (1)
{
sem_wait(&full); // full initially is = 0, so it blocks at first
//pthread_mutex_lock(&lock);
int item = buffer[out];
printf("Remove Item %d from %d\n",item, out);
//pthread_mutex_unlock(&lock);
sem_post(&empty);
printf(" %d", out); //using the values in the buffer
out = (out+1)%BufferSize;
}
}
int main()
{
//pthread_mutex_init(&lock, NULL);
sem_init(&empty,0,BufferSize);
sem_init(&full,0,0);
pid_t id1, id2;
id1 = fork();
if (id1 == 0) {
id2 = fork();
if (id2 == 0) //child 2
{
printf("im the consumer");
consumer();
}else{ //child 1
printf("i'm the producer");
producer();
}
}else{
sem_destroy(&full);
sem_destroy(&empty);
//pthread_mutex_destroy(&lock);
return 0;
}
}
thank you so much
By default, different processes live in different virtual address spaces. They cannot access each other's memory. The same is true for POSIX semaphores. This means that when you use fork to make two processes, you end up with two different sets of variables. You have a separate buffer and semaphores for each process.
To make this work, you should create a memory mapping (mmap for POSIX) where you put all the data you need to share between the processes, including the semaphores. The semaphores should be initialized with pshared to non-zero. The man page for sem_init has more details about this.
Alternatively you could use some other form of inter-process communication, like pipes.
I'm currently writing a program that takes an array that has randomly generated numbers, and uses multithreading to essentially divide the array in equal parts, then each thread will find the minimum of their respective division of the array. Essentially, I need my parent thread to be blocked (have non-busy waiting for parallel processing efficiency) using semaphores while the child threads are looking for the minimum, however the combination of sem_wait and sem_post is not blocking the parent thread as expected.
I've attempted to change the sem_init parameters to different values, however it seems no matter what I do the parent doesn't actually get blocked.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/timeb.h>
#include <semaphore.h>
#include <stdbool.h>
#include <unistd.h>
#define MAX_SIZE 100000000
#define MAX_THREADS 16
#define RANDOM_SEED 8631
#define MAX_RANDOM_NUMBER 5000
// Global variables
long gRefTime; //For timing
int gData[MAX_SIZE]; //The array that will hold the data
int gThreadCount; //Number of threads
int gDoneThreadCount; //Number of threads that are done at a certain point. Whenever a thread is done, it increments this. Used with the semaphore-based solution
int gThreadMin[MAX_THREADS]; //The minimum value found by each thread
bool gThreadDone[MAX_THREADS]; //Is this thread done? Used when the parent is continually checking on child threads
int indices[MAX_THREADS][3];
// Semaphores
sem_t completed; //To notify parent that all threads have completed or one of them found a zero
sem_t mutex; //Binary semaphore to protect the shared variable gDoneThreadCount
int main(int argc, char *argv[]){
pthread_t tid[MAX_THREADS];
pthread_attr_t attr[MAX_THREADS];
int i, indexForZero, arraySize, min;
// Code for parsing and checking command-line arguments
if(argc != 4){
fprintf(stderr, "Invalid number of arguments!\n");
exit(-1);
}
if((arraySize = atoi(argv[1])) <= 0 || arraySize > MAX_SIZE){
fprintf(stderr, "Invalid Array Size\n");
exit(-1);
}
gThreadCount = atoi(argv[2]);
if(gThreadCount > MAX_THREADS || gThreadCount <=0){
fprintf(stderr, "Invalid Thread Count\n");
exit(-1);
}
indexForZero = atoi(argv[3]);
if(indexForZero < -1 || indexForZero >= arraySize){
fprintf(stderr, "Invalid index for zero!\n");
exit(-1);
}
GenerateInput(arraySize, indexForZero);
CalculateIndices(arraySize, gThreadCount, indices);
InitSharedVars();
SetTime();
// Initialize threads, create threads, and then make the parent wait on the "completed" semaphore
// The thread start function is ThFindMinWithSemaphore
sem_init(&mutex, 0, 1);
sem_init(&completed, 0, 0);
for(i=0; i < gThreadCount; i++){
pthread_attr_init(&attr[i]);
pthread_create(&tid[i],&attr[i],ThFindMinWithSemaphore,&indices[i]);
}
sem_wait(&completed);
if(gThreadDone[i] == true && gThreadMin[i] == min){
for(i=0; i < gThreadCount; i++){
pthread_cancel(tid[i]);
}
}
min = SearchThreadMin();
printf("Threaded FindMin with parent waiting on a semaphore completed in %ld ms. Min = %d\n", GetTime(), min);
void* ThFindMinWithSemaphore(void *param) {
int threadNum = ((int*)param)[0];
int i;
int startIndex = indices[threadNum][1];
int endIndex = indices[threadNum][2];
sem_wait(&completed);
for (i = startIndex; i < endIndex; i++) {
if (gData[i] < gThreadMin[threadNum]){
gThreadMin[threadNum] = gData[i];
}
else if (gData[i] == 0){
sem_post(&completed);
pthread_exit(0);
}
}
sem_wait(&mutex);
gDoneThreadCount++;
sem_post(&mutex);
if (gDoneThreadCount == gThreadCount){
sem_post(&completed);
}
pthread_exit(0);
}
Note that this is not all of the file code that is actually in the file.
I want the main function to wait at the line where it says sem_wait(&completed). The goal is to have the child threads signal the parent by using sem_post when each thread is done searching for their minimum value, or one of the threads has found a zero within the array. Then at that point the main function should continue after receiving that sem_post signal.
As I understand, if the semaphore completed has a count of zero which I have initialized it to that using sem_init(&completed, 0, 0), the caller for sem_wait waits until it receives sem_post from one of the child threads. It appears that my program does not do the wait as expected.
In relation to when producer accesses the shared source, Do I need another condition variable/condition signal? So it does get blocked when the shared resource is locked. I have a condition variable for the consumer, so it does it waits and if it won't try to access if there is nothing there to be added on (to consume)
int pnum; // number updated when producer runs.
int csum; // sum computed using pnum when consumer runs.
int (*pred)(int); // predicate indicating number to be consumed
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condc,condp;
int toConsume = 0; // condition varibale counter
int produceT() {
//Start Critical Section
pthread_mutex_lock(&mutex);
scanf("%d",&pnum);
toConsume++;
//End Critical Section
pthread_cond_signal (&condc);
pthread_mutex_unlock(&mutex);
return pnum;
}
void *Produce(void *a) {
int p;
p=1;
while (p) {
printf("producer thinking...\n");
sleep(1);
printf("..done!\n");
p = produceT();
printf("PRODUCED %d\n",p);
}
printf("EXIT-P\n");
pthread_exit(0);
}
int consumeT() {
pthread_mutex_lock(&mutex); //protect buffer
while(toConsume <=0){ // if nothing in buffer then wait
pthread_cond_wait(&condc,&mutex);
}
pthread_mutex_unlock(&mutex); //release buffer
//sleep()
pthread_mutex_lock(&mutex); //protect buffer
if ( pred(pnum) ) { csum += pnum; }
toConsume--;
pthread_mutex_unlock(&mutex);
return pnum;
}
void *Consume(void *a) {
int p;
p=1;
while (p) {
printf("consumer thinking...\n");
sleep(rand()%3);
printf("..done!\n");
p = consumeT();
printf("CONSUMED %d\n",csum);
}
printf("EXIT-C\n");
pthread_exit(0);
}
int main (int argc, const char * argv[]) {
// the current number predicate
static pthread_t prod,cons;
long rc;
pred = &cond1;
if (argc>1) {
if (!strncmp(argv[1],"2",10)) { pred = &cond2; }
else if (!strncmp(argv[1],"3",10)) { pred = &cond3; }
}
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&condc,NULL);//Initialize consumer condition variable
pthread_cond_init(&condp,NULL);//Initialize producer condition variable
pnum = 999;
csum=0;
srand(time(0));
printf("Creating Producer:\n");
rc = pthread_create(&prod,NULL,Produce,(void *)0);
if (rc) {
printf("ERROR return code from pthread_create(prod): %ld\n",rc);
exit(-1);
}
printf("Creating Consumer:\n");
rc = pthread_create(&cons,NULL,Consume,(void *)0);
if (rc) {
printf("ERROR return code from pthread_create(cons): %ld\n",rc);
exit(-1);
}
pthread_join( prod, NULL);
pthread_join( cons, NULL);
printf("csum=%d.\n",csum);
return 0;
}
A conditional variable is a way of efficiently blocking a thread until a certain condition is verified.
You use a conditional variable in the consumer simply because you dictate you can't consume if there isn't anything to consume -- so you decide to block until there is something to consume. In your case, this happens when toConsume == 0.
You may dictate that the producer must also wait -- that's entirely up to your specification. Some ideas:
You may want to prevent the variable to go over a certain value (e.g. 1000, or INT_MAX to avoid overflow).
If instead of a counter you use a circular buffer to place your things to be consumed, the producer must wait if there are no free slots to place the things in the buffer. This is the most common way of teaching the producers-consumers in textbooks :)
I am new to semaphores and wanted to know how to avoid a situation where my worker thread (in a multiple worker single producer problem) is stuck in sem_wait(&full). I also have a mutex lock after this but I doubt that, that is where it is stuck. I know all my tasks are being completed but the threads are not exiting. The producer thread in my program stops sending data once all tasks are done.
For reference... the producer thread...
while (i<number_of_tasks)
{
TASK task;
task.start_row = i*num_of_rows;
task.num_of_rows = num_of_rows;
sem_wait(&empty);
pthread_mutex_lock(&mutex);
tasks[in] = task;
in = (in+1)%buff_size;
pthread_mutex_unlock(&mutex);
sem_post(&full);
i++;
//printf("%d tasks completed out of %d tasks\n", counter, number_of_tasks);
if (counter==number_of_tasks)
break;
}
And ...the worker thread function
int i, item, index;
TASK task;
index = *((int*)arg);
int notasks1;
for (i=number_of_tasks; i > 0; i--) {
//previous code
sem_wait(&full);
pthread_mutex_lock(&mutex);
task=tasks[out];
out = (out+1)%buff_size;
//completing task code...
counter++;
pthread_mutex_unlock(&mutex);
sem_post(&empty);
printf("%d tasks completed out of %d tasks\n", counter, number_of_tasks);
if (counter==number_of_tasks)
break;
}
printf("Worker thread %d has terminated and completed %d tasks\n",index,notasks1);
return NULL;
the full code... for reference
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include </usr/include/semaphore.h>
// for sleep
#include <unistd.h>
#include "Mandel.h"
#include "draw.h"
#define BUFF_SIZE 10 /* total number of slots */
#define NP 1 /* total number of producers */
#define NC 5 /* total number of consumers */
#define NITERS IMAGE_HEIGHT/10 /* number of items produced/consumed */
//int buf[BUFF_SIZE]; /* shared var */
//struct task for storing the tasks the current worker thread has to do
typedef struct task
{
int start_row;
int num_of_rows;
} TASK;
TASK* tasks;
int buff_size; //= 7;
//TASK buff[buff_size];
int in=0; /* buf[in%BUFF_SIZE] is the first empty slot */
int out=0; /* buf[out%BUFF_SIZE] is the first full slot */
sem_t full; /* keep track of the number of full spots */
sem_t empty; /* keep track of the number of empty spots */
int number_of_tasks;// = IMAGE_HEIGHT/20;
float *pixels;
int num_of_rows;// = 20;
int counter = 0;
// use correct type here
pthread_mutex_t mutex; /* enforce mutual exclusion to shared data */
/*void *Producer(void *arg)
{
int i =0;
while (i<number_of_tasks)
{
if (counter==number_of_tasks)
break;
TASK task;
task.start_row = i*num_of_rows;
task.num_of_rows = num_of_rows;
sem_wait(&empty);
pthread_mutex_lock(&mutex);
tasks[in] = task;
in = (in+1)%buff_size;
pthread_mutex_unlock(&mutex);
sem_post(&full);
i++;
if (counter==number_of_tasks)
break;
}
return NULL;
}*/
//function for consumer threads to accesss
void *Consumer(void *arg)
{
int i, item, index;
TASK task;
index = *((int*)arg);//getting index of thread from argument parsing it into int
int notasks1;
//for loop to complete number of tasks per thread
for (i=number_of_tasks; i > 0; i--) {
//defining start_compute and end compute to calculate time per task
struct timespec start_compute, end_compute;
float difftime;
printf("Worker(%d): Start the computation ...\n", index);
clock_gettime(CLOCK_MONOTONIC, &start_compute);
//calling sem wait to wait for producer thread to fill in tasks for worker threads. will wait if buffer is empty
sem_wait(&full);
//calling mutex lock
pthread_mutex_lock(&mutex);
//getting tasks from struct tasks array
task=tasks[out];
//incrementing to next task in buffer so that next thread accesses next task
out = (out+1)%buff_size;
//calculating pixels for image
int x, y;
//printf("start_row... %d, num_of_rows... %d\n", task.start_row, task.num_of_rows);
for (y=task.start_row; y<task.start_row+task.num_of_rows; y++)
{
for (x=0; x<IMAGE_WIDTH; x++)
{
pixels[y*IMAGE_WIDTH+x] = Mandelbrot(x, y);
}
}
//increasing counter to check if all tasks are done
counter++;
pthread_mutex_unlock(&mutex);
sem_post(&empty);//informing a producer that a task has been taken out and if tasks are left buffer is empty to add tasks
//calculating end compute and posting
clock_gettime(CLOCK_MONOTONIC, &end_compute); //end time values for the child processes
difftime = (end_compute.tv_nsec - start_compute.tv_nsec)/1000000.0 + (end_compute.tv_sec - start_compute.tv_sec)*1000.0;
printf("Worker(%d):\t...completed. Elapse time = %f ms\n",index, difftime);\
notasks1++;
printf("%d tasks completed out of %d tasks\n", counter, number_of_tasks);
if (counter==number_of_tasks)
break;
}
printf("Worker thread %d has terminated and completed %d tasks\n",index,notasks1);
return NULL;
}
int main(int argc, char* args[])
{
buff_size = atoi(args[3]);
num_of_rows = atoi(args[2]);
number_of_tasks = IMAGE_HEIGHT/num_of_rows;
int number_of_workers;
number_of_workers = atoi(args[1]);
struct timespec start_time, end_time;
printf("number_of_tasks...%d\n", number_of_tasks);
pthread_t idP, idC;
int index;
sem_init(&full, 0, 0);//initializing full to zero so that consumer thread knows buffer is empty and waits for producer thread
sem_init(&empty, 0, buff_size);//initializing empty to buffer size so that producer knows to add that many tasks
pthread_mutex_init(&mutex, NULL);
int i = 0;
pixels = (float *) malloc(sizeof(float) * IMAGE_WIDTH * IMAGE_HEIGHT);//assigning pixels memory
//tasks = (TASK*) malloc (sizeof(TASK) *buff_size);
//pthread_create(&idP, NULL, Producer, NULL);
for(index=0; index<number_of_workers/*5*/; index++)
{
printf("Worker(%d): Start up. Wait for task!\n", index);
pthread_create(&idC, NULL, Consumer, (void*)&index);//creating worker threads to go to consumer function sending their index
}
tasks = (TASK*) malloc (sizeof(TASK) *buff_size);
//master/producer thread work
while (i<number_of_tasks)
{
TASK task;
task.start_row = i*num_of_rows;
task.num_of_rows = num_of_rows;
//calling sem wait if buffer is not empty
sem_wait(&empty);
pthread_mutex_lock(&mutex);
//adding tasks to struct task tasks array for worker/consumer threads to use
tasks[in] = task;
in = (in+1)%buff_size;
pthread_mutex_unlock(&mutex);
sem_post(&full);
//incrementing full so that worker threads know that buffer is not empty
i++;
//printf("%d tasks completed out of %d tasks\n", counter, number_of_tasks);
if (counter==number_of_tasks)
break;
}
//pthread_exit(NULL);
//waiting for all threads to exit
//pthread_join(idC, NULL);
//printing image
printf("Draw the image\n");
DrawImage(pixels, IMAGE_WIDTH, IMAGE_HEIGHT, "Mandelbrot demo", 3000);
return 0;
}
How to stop the worker thread from being stuck in the loop?
I know the program continues on and displays the correct result but when I call pthread_join it keeps on waiting... obviously. How to solve the issue?
I have a multiple read threads and one write thread. If I lock mutex on one of the read threads and send broadcast from it, is it guaranteed that mutex will be locked by write thread waiting on pthread_cond_wait() or is there a possibility that another read thread that is wainting on pthread_mutex_lock() will lock mutex? Main question is does pthread_cond_wait() have priority over pthread_mutex_lock()?
If not, how can I achieve that the mutex will always be locked by write thread on pthread_cond_broadcast()?
Example
Read thread:
pthread_mutex_lock(mutex);
pthread_cond_broadcast(cond);
pthread_mutex_unlock(mutex);
Write thread:
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
Let's assume both threads, read and write, reach the pthread_mutex_lock in the same moment. So, either write thread acquire the mutex on pthread_mutex_lock call, or read thread.
If it would be the write thread, the read one will wait on pthread_mutex_lock. The write, by calling pthread_cond_wait releases mutex and blocks on cond. It is done atomically. So, when read thread is grantex the mutex, we can be sure the the read one waits on cond. So, broadcast on cond reaches the write thread, it no more waits on cond but - still in scope of pthread_cond_wait - tries to get a lock on mutex (hold be read thread). After broadcasting cond the read thread releases the mutex and it goes to write thread. So write thread finally exits from pthread_cond_wait having the mutex locked. Remember to unlock it later.
If it would be the read thread, the write one will wait on pthread_mutex_lock, the read will broadcast a signal on cond then release the mutex. After then the write thread acquires the mutex on pthread_mutex_lock and immediately releases in it pthread_cond_wait waiting for cond (please note, that previous cond broadcast has no effect on current pthread_cond_wait). In the next iteration of read thread it acquires lock onmutex, send broadcast on cond and unlock mutex. It means the write thread moves forward on cond and acquires lock on mutex.
Does it answer your question about priority?
Update after comment.
Let's assume we have one thread (let's name it A for future reference) holding the lock on mutex and few other trying to acquire the same lock. As soon as the lock is released by first thread, there is no predictable which thread would acquire lock. Moreover, if the A thread has a loop and tries to reacquire lock on mutex, there is a chance it would be granted this lock and other threads would keep waiting. Adding pthread_cond_wait doesn't change anything in scope of granting a lock.
Let me quote fragments of POSIX specification (see https://stackoverflow.com/a/9625267/2989411 for reference):
These functions atomically release mutex and cause the calling thread to block on the condition variable cond; atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable". That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_broadcast() or pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.
And this is only guarantee given by standard regarding order of operations. Order of granting the lock to other threads is rather unpredictable and it changes depending on some very subtle fluctuation in timing.
For only mutex related code, please play a little with following code:
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *th(void *arg) {
int i;
char *s = arg;
for (i = 0; i < 10; ++i) {
pthread_mutex_lock(&mutex);
printf("%s %d\n", s, i);
//sleep(1);
pthread_mutex_unlock(&mutex);
#if 0
pthread_yield();
#endif
}
return NULL;
}
int main() {
int i;
for (i = 0; i < 10; ++i) {
pthread_t t1, t2, t3;
printf("================================\n");
pthread_create(&t1, NULL, th, "t1");
pthread_create(&t2, NULL, th, " t2");
pthread_create(&t3, NULL, th, " t3");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
}
return 0;
}
On one machine (single CPU) it always shows whole loop from t3, then t2 and finally from t1. On another (2 cores) the order of threads is more random, but almost always it shows whole loop for each thread before granting the mutex to other thread. Rarely there is a situation like:
t1 8
t1 9
t3 0
t2 0
t2 1
[removed other t2 output]
t2 8
t2 9
t3 1
t3 2
Enable pthread_yield by replacing #if 0 with #if 1 and watch results and check output. For me it works in a way two threads display their output interlaced, then third thread finally has a chance to work. Add another or more thread. Play with sleep, etc. It confirms the random behaviour.
If you wish to experiment a little, compile and run following piece of code. It's an example of single producer - multiple consumers model. It can be run with two parameters: first is the number of consumer threads, second is the length of produced data series. If no parameters are given there is one consumer thread and 120 items to be processed. I also recommend with sleep/usleep in places marked /* play here */: change the value of arguments, remove the sleep at all, move it - when appropriate - to critical section or replace with pthread_yield and observe changes in behaviour.
#define _GNU_SOURCE
#include <assert.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
struct data_t {
int seq;
int payload;
struct data_t *next;
};
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
struct data_t *first = NULL, *last = NULL;
int in_progress = 1;
int num_data = 120;
void push(int seq, int payload) {
struct data_t *e;
e = malloc(sizeof(struct data_t));
e->seq = seq;
e->payload = payload;
e->next = NULL;
if (last == NULL) {
assert(first == NULL);
first = last = e;
} else {
last->next = e;
last = e;
}
}
struct data_t pop() {
struct data_t res = {0};
if (first == NULL) {
res.seq = -1;
} else {
res.seq = first->seq;
res.payload = first->payload;
first = first->next;
if (first == NULL) {
last = NULL;
}
}
return res;
}
void *producer(void *arg __attribute__((unused))) {
int i;
printf("producer created\n");
for (i = 0; i < num_data; ++i) {
int val;
sleep(1); /* play here */
pthread_mutex_lock(&mutex);
val = rand() / (INT_MAX / 1000);
push(i, val);
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
printf("prod %3d %3d signaled\n", i, val);
}
in_progress = 0;
printf("prod end\n");
pthread_cond_broadcast(&cond);
printf("prod end signaled\n");
return NULL;
}
void *consumer(void *arg) {
char c_id[1024];
int t_id = *(int *)arg;
sprintf(c_id, "%*s c %02d", t_id % 10, "", t_id);
printf("%s created\n", c_id);
while (1) {
struct data_t item;
pthread_mutex_lock(&mutex);
item = pop();
while (item.seq == -1 && in_progress) {
printf("%s waits for data\n", c_id);
pthread_cond_wait(&cond, &mutex);
printf("%s got signal\n", c_id);
item = pop();
}
if (!in_progress && item.seq == -1) {
printf("%s detected end of data.\n", c_id);
pthread_mutex_unlock(&mutex);
break;
}
pthread_mutex_unlock(&mutex);
printf("%s processing %3d %3d\n", c_id, item.seq, item.payload);
sleep(item.payload % 10); /* play here */
printf("%s processed %3d %3d\n", c_id, item.seq, item.payload);
}
printf("%s end\n", c_id);
return NULL;
}
int main(int argc, char *argv[]) {
int num_cons = 1;
pthread_t t_prod;
pthread_t *t_cons;
int i;
int *nums;
if (argc > 1) {
num_cons = atoi(argv[1]);
if (num_cons == 0) {
num_cons = 1;
}
if (num_cons > 99) {
num_cons = 99;
}
}
if (argc > 2) {
num_data = atoi(argv[2]);
if (num_data < 10) {
num_data = 10;
}
if (num_data > 600) {
num_data = 600;
}
}
printf("Spawning %d consumer%s for %d items.\n", num_cons, num_cons == 1 ? "" : "s", num_data);
t_cons = malloc(sizeof(pthread_t) * num_cons);
nums = malloc(sizeof(int) * num_cons);
if (!t_cons || !nums) {
printf("Out of memory!\n");
exit(1);
}
srand(time(NULL));
pthread_create(&t_prod, NULL, producer, NULL);
for (i = 0; i < num_cons; ++i) {
nums[i] = i + 1;
usleep(100000); /* play here */
pthread_create(t_cons + i, NULL, consumer, nums + i);
}
pthread_join(t_prod, NULL);
for (i = 0; i < num_cons; ++i) {
pthread_join(t_cons[i], NULL);
}
free(nums);
free(t_cons);
return 0;
}
I hope I have cleared your doubts and gave you some code to experiment and gain some confidence about pthread behaviour.