2016-05-24 10 views
0

Ich mache Multithread-Programm mit Pthreads. Die Idee ist einfach:Pthreads - Produzent und Verbraucher mit Zustandsvariable und Mutex - Join Fehler und seltsame Cout

  • Car (Gewinde)
  • Benzinversorgung (Gewinde)
  • Tankstelle (resource)

Beide Auto und Tankstelle etwas Kraftstoff Kapazität haben und nach dem Auto aus Treibstoff muss Tankstelle besuchen. Nachdem die Tankstelle keinen Kraftstoff mehr hat, läuft der Benzinversorgungsfaden und füllt die Ressource nach. Alles scheint gut zu sein, außer dass ich pthread_exit anstelle von pthread_join verwenden muss, um auf Threads in der Hauptfunktion zu warten und manchmal doppelt cout für das gleiche Auto vorkommt: "-----End of fuel-----". Mache ich es richtig?

Structs und einige globale Variablen:

#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; 
}; 

Themen:

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; 
} 

Ausgabebeispiel:

-----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 
(...) 

Und die Frage ist, warum Ausgang nicht so aussieht? Manchmal sind fünf Iterationen in Ordnung und sechs zeigt doppelte Nachricht. Und was ist los mit Join? Danke für Ratschläge.

+0

Es ist ein bisschen komisch, dass Sie sich der Notwendigkeit bewusst sind, Ihren 'pthread_cond_t' zu initialisieren, aber scheinen, Inhalt Ihren' pthread_mutex_t 's-Standard-initialisiert zu halten. Außerdem scheinen Sie 'pthread_cont_t' als Semaphor zu glauben. Es ist nicht. – EOF

+0

Danke für die Antwort und Ihre Anmerkungen, fehlende Mutex-Initialisierung hinzugefügt. Nicht sicher, wie man den zweiten Teil deines Ratschlags benutzt, weil ich andere Stack-Themen untersuchte. Leute haben vorgeschlagen, 'pthread_mutex_t' und' pthread_cont_t' auf diese Weise zu mischen, aber ich könnte falsch liegen. – Macieyo

+1

Hinweis: 1. Parameter von pthread_join() ist eine pthread_id, aber Sie haben eine pthread_t zur Verfügung gestellt. Nicht das Gleiche. (Dies ist keine Antwort, weil es nicht seine (primäre) Frage ist.) –

Antwort

0

Das Auto kann "Ende des Kraftstoffs" mehrfach anzeigen, weil, wenn es findet, der petrolDistributor fast leer ist, es nicht wartet - es geht um die While Schleife wieder herum, ohne sich selbst zu tanken.

Was sollten Sie hier tun ist mit den Auto Warten auf einer Zustandsgröße, wenn es den petrolDistributor mit unzureichendem Kraftstoff für das Auto zu tanken findet. Es sollte nicht fortgesetzt werden, bis der petrolDistributor hat betankt worden:

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); 

ich carwaiting hier eine neue conditiona Variable verwendet habe. Beachten Sie, dass der boolesche Wert petrolDistributor.isEmpty nicht erforderlich ist - dieses Prädikat ist nur petrolDistributor.petrol < 30.

Die petrolDistributor braucht keine wartenden Autos, um zu signalisieren, nachdem es die Station füllt:

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); 

Hinweis I pthread_cond_broadcast() hier verwendet haben, denn wenn man diese verlängern mehrere Autos zu schließen, mehr als eine für vielleicht warten Treibstoff.

Ihre pthread_join() Anrufe sollten einfach sein:

pthread_join(car, NULL); 
pthread_join(supply, NULL); 

(pthread_join() nimmt ein pthread_t als Argument, nicht pthread_t *).