randomizertech
randomizertech

Reputation: 2329

Multithreading in C not working properly

So I managed to get my program running but for some reason, no customer walks in the bar unless the customer before him/her leaves the bar first, how can I fix it? I tried a mutex_lock but I might've implemented it wrong, here is what I have from my code so far:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

sem_t sem;
pthread_mutex_t serve = PTHREAD_MUTEX_INITIALIZER;

void Bartender(int);
void EnterBar(int);
void OrderStart(int);
void ServeStart(int);
void ServeDone(int);
void OrderDone(int);
void DrinkBeer(int);
void LeaveBar(int);
void* Customer(void*);

void Bartender(int cid)
{
    ServeStart(cid);
    ServeDone(cid);
}

void* Customer(void* id)
{
    int cid =(int)id;
    EnterBar(cid);
    LeaveBar(cid);
    return NULL;

}

void EnterBar(int cid){
    sem_wait(&sem); //decrease semaphore
    printf("Customer %d enters the bar.\n", cid);
    int cups;

    for(cups=0;cups<(cid%3+1);cups++){
        pthread_mutex_lock(&serve);     
        OrderStart(cid);
        OrderDone(cid);
        DrinkBeer(cid);     
        pthread_mutex_unlock(&serve);
    }
}
void OrderStart(int cid)
{
    printf("Customer %d asks for beer.\n", cid);
    Bartender(cid);
}

void OrderDone(int cid)
{
    printf("Customer %d gets the beer.\n", cid);
}

void DrinkBeer(int cid)
{
    printf("Customer %d drinks the beer.\n", cid);
}

void LeaveBar(int cid)
{
    printf("Customer %d leaves the bar.\n", cid);
    sem_post( &sem ); //increase semaphore
}

void ServeStart(int cid)
{
    printf("Bartender starts to serve customer %d.\n", cid);
}

void ServeDone(int cid)
{
    printf("Bartender is done serving customer %d.\n", cid);
}

int main (int argc, char *argv[])
{
    int t;
    long rc;    
    int num_customers = atoi(argv[1]); //number of customers
    int capacity = atoi(argv[2]); //bar capacity

    if(num_customers > 0 && capacity > 0){
        rc = sem_init( &sem, 0, capacity );
        if (rc)
        {
            printf("ERROR; return code from sem_init() is %ld\n",rc);
            exit(-1);
        }
        //pthread_t threads[num_customers];
        pthread_t *threads = (pthread_t*)malloc(num_customers*sizeof(pthread_t));
        if(random() > RAND_MAX / 2)
            usleep(1);
        //rc = sem_init(&sem1,0,capacity);
        //rc = pthread_barrier_init(&barrier1, NULL, num_customers);
        for(t=0; t<num_customers;t++){
                printf("In main: creating thread %d\n", t);
            //printf("CAPACITY: %d\n", capacity);
                rc = pthread_create(&threads[t], NULL, Customer, (void* )t);
                if (rc){
                    printf("ERROR; return code from pthread_create() is %ld\n", rc);
                    exit(-1);
                }
        }
        for( t=0;t<num_customers;t++)
            pthread_join(threads[t],NULL);

        sem_destroy(&sem); //destroy semaphore
    }
    else{
            printf("ERROR: Both parameters should be a valid positive numbers.");
            exit(-1);
    }

    /* Last thing that main() should do */
    pthread_exit(NULL);
}

It should allow multiple customers in the bar, and to leave after the finish drinking their beers, not after the customer before them leaves. Do I need a thread for the bartender? Any suggestions??

Upvotes: 0

Views: 484

Answers (4)

Martin James
Martin James

Reputation: 24847

What's with all this mutex gunge? What's wrong with a couple semaphores, one 'barAccess', initialized to the bar capacity, one 'barStaff', init. to the number of bartenders?

#define slaughtered (beerCount==10)

{
  beerCount=0;
  wait(barAccess);
  do{
    wait(barStaff);
    sleep(pouringTime);
    signal(barstaff);
    sleep(drinkingTime);
    beerCount++;
  }while(!slaughtered);
  signal(barAccess);
};

If this bar has regular customers, you can add another while(true) loop round it with a 'sleep(soberUp);' after each session in the boozer.

It's also easy to vary the number of barstaff during opening time. Just add more 'barStaff' units or steal some away.

If it gets really busy, you might want to open the lounge bar as well to increase capacity - just all more barAccess units in a loop.

Closing time should be fun. You need to persuade the customers to leave and not allow any more in. Maybe the customers should leave if they don't get served in a reasonable time. You could then suck out all the barstaff so the customers can't get served and gather in all the barAccess units so new customers can't get in. Eventually, the bar will be empty for today. When you open up the next day, shove in the barstaff first, then the barAccess units and watch the rush to get served!

Upvotes: 1

Duck
Duck

Reputation: 27542

You don't have the problem you think you have. I made the following changes.

(1) //add globals

int count = 0;
pthread_mutex_t mutCount = PTHREAD_MUTEX_INITIALIZER;

(2) Add to counter of people in bar and print out

void EnterBar(int cid)
{
    sem_wait(&sem); //decrease semaphore

    printf("Customer %d enters the bar.\n", cid);    

    pthread_mutex_lock(&mutCount);
    count++;
    printf("There are %i customers in the bar\n", count);
    pthread_mutex_unlock(&mutCount);

    int cups;

    for (cups = 0;cups < (cid % 3 + 1);cups++)
    {
        pthread_mutex_lock(&serve);
        OrderStart(cid);
        OrderDone(cid);
        DrinkBeer(cid);
        pthread_mutex_unlock(&serve);
    }
}

(3) decrement count of customers in bar when they leav

void LeaveBar(int cid)
{
    printf("Customer %d leaves the bar.\n", cid);

    pthread_mutex_lock(&mutCount);
    count--;
    pthread_mutex_unlock(&mutCount);

    sem_post( &sem ); //increase semaphore
}

(4) In main change usleep to something worthwhile

    if (random() > RAND_MAX / 2)
        usleep(100);

(5) I ran with: barthread 20 15 | grep "There are"

(6) I get the following output:

There are 1 customers in the bar
There are 2 customers in the bar
There are 1 customers in the bar
There are 3 customers in the bar
There are 4 customers in the bar
There are 5 customers in the bar
There are 6 customers in the bar
There are 7 customers in the bar
There are 6 customers in the bar
There are 6 customers in the bar
There are 5 customers in the bar
There are 5 customers in the bar
There are 5 customers in the bar
There are 6 customers in the bar
There are 4 customers in the bar
There are 4 customers in the bar
There are 5 customers in the bar
There are 6 customers in the bar
There are 7 customers in the bar
There are 8 customers in the bar

Upvotes: 1

amb
amb

Reputation: 1609

I think this is a correct behavior, right ? Customer 2 is the first in the bar but leaves after customer 1.

[amb@localhost ~]$ ./a.out 3 2
In main: creating thread 0
In main: creating thread 1
In main: creating thread 2
Customer 2 enters the bar.
Customer 2 asks for beer.
Bartender starts to serve customer 2.
Bartender is done serving customer 2.
Customer 2 gets the beer.
Customer 2 drinks the beer for 7 seconds.
Customer 1 enters the bar.
Customer 1 asks for beer.
Bartender starts to serve customer 1.
Bartender is done serving customer 1.
Customer 1 gets the beer.
Customer 1 drinks the beer for 8 seconds.
Customer 2 asks for beer.
Bartender starts to serve customer 2.
Bartender is done serving customer 2.
Customer 2 gets the beer.
Customer 2 drinks the beer for 6 seconds.
Customer 1 asks for beer.
Bartender starts to serve customer 1.
Bartender is done serving customer 1.
Customer 1 gets the beer.
Customer 1 drinks the beer for 4 seconds.
Customer 1 leaves the bar.
Customer 0 enters the bar.
Customer 0 asks for beer.
Bartender starts to serve customer 0.
Bartender is done serving customer 0.
Customer 0 gets the beer.
Customer 0 drinks the beer for 6 seconds.
Customer 2 asks for beer.
Bartender starts to serve customer 2.
Bartender is done serving customer 2.
Customer 2 gets the beer.
Customer 2 drinks the beer for 7 seconds.
Customer 0 leaves the bar.
Customer 2 leaves the bar.

Upvotes: 1

amb
amb

Reputation: 1609

#include <unistd.h>

and modify this functions

void DrinkBeer(int cid)
{
    printf("Customer %d drinks the beer for 10 seconds.\n", cid);
    sleep(10);
}
void LeaveBar(int cid)
{
    printf("Customer %d leaves the bar (takes 10 sec).\n", cid);
    sem_post( &sem ); //increase semaphore
   sleep(10);
}

See how it behaves now. You need some sleep functions in there so the thread can sleep and to observe better how the other threads are working. Does it function as it should with this modifications ?

Upvotes: 2

Related Questions