I am making multithread program using pthreads. The idea is simple:
Both car and petrol station have some fuel capacity and after car is out of fuel needs to visit petrol station. After petrol station is out of fuel, petrol supply thread runs and refills resource. Everything seems to be fine excluding that I have to use pthread_exit
instead of pthread_join
to wait for threads in main function and sometimes double cout
for the same car occurs: "-----End of fuel-----"
. Am I doing it right?
Structs and some global variables:
#define initialFuel 100
#define loop 10
pthread_mutex_t mutex1, mutex2;
pthread_cond_t isempty;
PetrolDistributor petrolDistributor;
struct Car {
int capacity = 10;
int petrol = 5;
};
struct PetrolDistributor {
int petrol = initialFuel;
bool isEmpty = false;
};
Threads:
void * threadSupply(void *arg )
{
for(int i = 0; i<loop; i++)
{
pthread_mutex_lock(&mutex1);
while(!petrolDistributor.isEmpty)
{
pthread_cond_wait(&isempty, &mutex1); //When signal received, do below:
usleep(2000000);
petrolDistributor.petrol = initialFuel; //Refill petrol and change state
petrolDistributor.isEmpty = false;
}
pthread_mutex_unlock(&mutex1);
}
}
void * threadPetrolDriver(void *arg )
{
Car *car;
car = (Car*) arg;
for(int i = 0; i<loop; i++)
{
while(car->petrol > 0) // Car consumes petrol here
{
usleep(200000);
cout << car->petrol << endl;
car->petrol -= 1;
}
cout << "-----End of fuel-----" << "\t\t #" << i << endl;
pthread_mutex_lock(&mutex1);
if (petrolDistributor.petrol >= 30) // If distributor almost empty?
{
petrolDistributor.petrol -= car->capacity; //Substract car's capacity amount of fuel from distributor
car->petrol = car->capacity; //Fillup mentioned capacity in car
}
else
{
petrolDistributor.isEmpty = true;
pthread_cond_signal(&isempty);
}
pthread_mutex_unlock(&mutex1);
}
}
Main:
int main()
{
pthread_t car;
pthread_t supply;
Car carPetrol;
pthread_cond_init(&isempty, NULL);
pthread_mutex_init(&mutex1, NULL);
pthread_create(&car, NULL, threadPetrolDriver, (void*) (&carPetrol));
pthread_create(&supply, NULL, threadSupply, NULL);
// pthread_join(&car, NULL); //results error: invalid conversion from ‘pthread_t* {aka long unsigned int*}’ to ‘pthread_t {aka long unsigned int}’ [-fpermissive]|
// pthread_join(&supply, NULL);
pthread_exit(NULL);
return 0;
}
Output example:
-----End of fuel----- #0
9
(...)
2
1
-----End of fuel----- #1
-----End of fuel----- #2 //Also for loop increments
10
9
(...)
3
2
1
-----End of fuel----- #3
10
9
(...)
And the question is why does output looks like that? Sometimes five iterations are fine and sixth shows double message. And what is wrong with join? Thanks for advices.
The car can show "End of fuel" multiple times because if it finds the petrolDistributor is nearly empty it doesn't wait - it goes around outer the while loop again, without refuelling itself.
What you should be doing here is having the car wait on a condition variable, if it finds the petrolDistributor with insufficient fuel to for the car to refuel. It shouldn't proceed until the petrolDistributor has been refuelled:
pthread_mutex_lock(&mutex1);
if (petrolDistributor.petrol < 30) // If distributor almost empty?
pthread_cond_signal(&isempty);
// wait for sufficient fuel to be available
while (petrolDistributor.petrol < car->capacity)
pthread_cond_wait(&carwaiting, &mutex1);
// refuel
petrolDistributor.petrol -= car->capacity; //Substract car's capacity amount of fuel from distributor
car->petrol = car->capacity; //Fillup mentioned capacity in car
pthread_mutex_unlock(&mutex1);
I've used a new conditiona variable carwaiting
here. Note that the petrolDistributor.isEmpty
boolean is unnecessary - that predicate is just petrolDistributor.petrol < 30
.
The petrolDistributor needs to signal any waiting cars after it refills the station:
pthread_mutex_lock(&mutex1);
// Wait for petrol to drop below low-water-mark
while(petrolDistributor.petrol >= 30)
pthread_cond_wait(&isempty, &mutex1);
usleep(2000000);
petrolDistributor.petrol = initialFuel; //Refill petrol and change state
pthread_cond_broadcast(&carwaiting); // Wake up any cars waiting for fuel
pthread_mutex_unlock(&mutex1);
Note I've used pthread_cond_broadcast()
here, because if you extend this to include multiple cars, more than one might be waiting for fuel.
Your pthread_join()
calls should just be:
pthread_join(car, NULL);
pthread_join(supply, NULL);
( pthread_join()
takes a pthread_t
as argument, not pthread_t *
).
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.