2016-09-23 1 views
-1

J'écris un code de base de producteur/consommateur. J'ai la plupart du temps pour fonctionner, mais j'ai un problème avec rand(). Ou peut-être mes problèmes sont plus profonds que rand(), et rand n'est que la pointe de l'iceberg. Je ne veux rien faire de trop complexe (pas de runnable ou d'attente).MultiThreading producteur/consommateur

J'ai un ensemble global d'entiers qui agit comme tampon. J'autorise l'utilisateur à saisir la taille et la limite des temps d'exécution. Je fais du compteur une variable globale statique. ceci est mon producteur:

DWORD WINAPI producer(LPVOID n) 
{ 
    cout << "\nPRODUCER:The producer is running right now" << endl; 
    int size = (int)n; 
    int num = rand()%10;// this is for making item. 
    while (buffer.size() > size) 
    { 
    } 

    buffer.push_back(num); 
    counter++; 
    return (DWORD)n; 

} 

c'est mon consumer--

DWORD WINAPI consumer(LPVOID n) 
{ 

    cout << "\nCONSUMER:The consumer is running right now" << endl; 
    while (buffer.empty()) 
    { } 
    int item= buffer.front(); 
    cout << "\nCONSUMER:The consumer ate" << item << endl; 
    counter++; 

    return (DWORD)n; 


    } 

en main-

while (counter < l) 
    { 
    hThreads[0] = CreateThread(NULL, 0, producer, (LPVOID)s, NULL, &id[0]); 
    hThreads[1] = CreateThread(NULL, 0, consumer, (LPVOID)l, NULL, &id[1]); 

    waiter = WaitForMultipleObjects(MAX_THREADS, hThreads, TRUE, INFINITE); 
    } 
    for (int i = 0; i < MAX_THREADS; i++) { 
    CloseHandle(hThreads[i]); 
    } 

Ma sortie est comme ceci: enter image description here

Il génère seulement 1 à chaque fois. Srand ne travaillait pas non plus. Mais il fonctionne le nombre correct de fois.

EDIT --- Je fixe le producteur et le consommateur pour faire face à la condition de course:

DWORD WINAPI producer(LPVOID s) 
    { 
    WaitForSingleObject(Empty, INFINITE);  
    WaitForSingleObject(Mutex, INFINITE); 
    cout << "\nPRODUCER...." << endl; 
    int size = (int)s; 
    srand(size); 
    int in = rand() % 10; 
    cout << "THIS IS IN:::" << in << endl; 
    while (buffer.size() == size) 
    { 
     ReleaseMutex(Mutex); 
     } 
    buffer.push_back(in); 
    counter++; 
    cout << "\nThe producer produces " << buffer.front() << endl; 
    ReleaseMutex(Mutex); 
    ReleaseSemaphore(Full, 1, NULL); 

    return (DWORD)s; 
    } 



    DWORD WINAPI consumer(LPVOID l) 
    { 
     WaitForSingleObject(Full, INFINITE); 
     WaitForSingleObject(Mutex, INFINITE); 
     cout << "\nCONSUMER...." << endl; 
     while (buffer.empty()) 
     { 
      ReleaseMutex(Mutex); 

     } 
    int out = buffer.front(); 
    counter++; 
    ReleaseMutex(Mutex); 
    ReleaseSemaphore(Empty, 1, NULL); 
    return (DWORD)l; 
    } 

mais la chose au hasard garde toujours agir vers le haut. Il ne fait que produire un nombre encore et encore (même lorsqu'il est ensemencé).

+0

Alors où est la synchronisation? – Mysticial

+0

Si le consommateur court le premier sans rien, alors le producteur intervient, puis le consommateur reprend là où il s'était arrêté. Cela dépend de ce qui commence –

+0

Vous avez une condition de course non protégée sur le tampon. C'est un comportement indéfini. – Mysticial

Répondre

0

Oui, cela n'a pas de sens de créer (et de détruire) un thread pour créer ou traiter un seul nombre - le surcoût supplémentaire ne vaut tout simplement pas. De plus, votre code (tel quel) a des erreurs ou des idées fausses assez apparentes. Ce sont:

  • Dans le thread principal vous créez les threads de travail (dans ce while() {} boucle), mais seulement les détruire une fois à la fin, qui vous détruire uniquement les poignées créées dans la dernière boucle.
  • Comme indiqué dans mon message, srand() est appelé pour que chaque nombre soit généré, et toujours avec la même graine initiale, donc c'est normal d'avoir les mêmes nombres.
  • Les boucles while() vérifiant si le tampon est vide ou plein n'ont pas de sens et ne doivent pas libérer le Mutex.
  • Les opérations sur la variable counter sont probablement erronées. Les threads producteur et consommateur l'augmentent et le thread principal l'utilise pour déterminer le nombre de nombres à générer/imprimer.
  • Dans votre fragment de code initial, counter est une variable globale dont plusieurs threads sont activés. Vous devez donc la lire ou la modifier de manière sûre pour les threads, mais pas de cette manière. Vous devez utiliser un mécanisme de verrouillage, comme une section critique ou un accès variable interlocké.

Ma suggestion serait de créer un fil de producteur (générer tous les nombres) et un fil de consommateur (impression de tous les nombres), transféré à travers le tampon. Pour cela, vous aurez besoin des éléments suivants (vous avez déjà implémenté la plupart d'entre eux):

  • Un sémaphore "complet", en comptant les nombres dans le tampon, initialement 0.
  • Sémaphore "Empty" complémentaire, en comptant les éléments vides dans le tampon, initialement défini sur la taille de la mémoire tampon. La "somme" de ces sémaphores devrait bien entendu toujours être égale à la taille de la mémoire tampon.
  • Un mutex (ou section critique), utilisé pour accéder au tampon.
  • Une variable globale, utilisée pour indiquer aux threads s'ils doivent quitter.

Je poste des exemples de code ci-dessous. Je ne sais pas si elles fonctionnent, vous devrez peut-être réviser ou de les déboguer, mais cela est juste pour mettre en valeur le concept:

// Global 
#define MAX_BUF 5 
BOOL bExit = FALSE; 

// Main Thread 
    Empty = CreateSemaphore(NULL, MAX_BUF, MAX_BUF, NULL); 
    Full = CreateSemaphore(NULL, 0, MAX_BUF, NULL); 
    . 
    . 
    hThreads[0] = CreateThread(NULL, 0, producer, (LPVOID)l, NULL, &id[0]); 
    hThreads[1] = CreateThread(NULL, 0, consumer, (LPVOID)l, NULL, &id[1]); 
    waiter = WaitForMultipleObjects(MAX_THREADS, hThreads, TRUE, INFINITE); 

    for (int i = 0; i < MAX_THREADS; i++) 
     CloseHandle(hThreads[i]); 


DWORD WINAPI producer(LPVOID nCount) 
{ 
    int nItems = (int)nCount; 
    // Initialize rand() seed - each run will be generating a different sequence 
    srand(GetTickCount()); // May need to AND GetTickCount() with RAND_MAX ??? 
    // Generate nCount numbers 
    for (int i = 0; i < nItems; i++) 
    { 
     if (bExit) return 9; // Aborted 
     WaitForSingleObject(Empty, INFINITE); // Wait until at least one item empty 
     // Lock the buffer and add an item 
     WaitForSingleObject(Mutex, INFINITE); // Could be EnterCriticalSection() instead 
     if (buffer.size() >= MAX_BUF) 
     { 
      cout << "\nInternal Error: Buffer-full Check Failed!" << endl; 
      bExit = TRUE; // Tell all threads to exit 
      ReleaseMutex(Mutex); 
      return 1; // Exit with Error 
     } 
     int in = rand() % 10; 
     buffer.push_back(in); 
     cout << "The Producer generated: " << in << endl; 
     ReleaseMutex(Mutex); // Could be LeaveCriticalSection() instead 
     ReleaseSemaphore(Full, 1, NULL); // 1 item added, update semaphore 
    } 
    cout << "\nThe PRODUCER produced " << nItems << " items." << endl; 
    return 0; // OK 
} 

DWORD WINAPI consumer(LPVOID nCount) 
{ 
    int nItems = (int)nCount; 
    // Generate nCount numbers 
    for (int i = 0; i < nItems; i++) 
    { 
     if (bExit) return 9; // Aborted 
     WaitForSingleObject(Full, INFINITE); // Wait until at least one item in buffer 
     // Lock the buffer and get an item 
     WaitForSingleObject(Mutex, INFINITE); // Could be EnterCriticalSection() instead 
     if (buffer.empty()) 
     { 
      cout << "\nInternal Error: Buffer-empty Check Failed!" << endl; 
      bExit = TRUE; // Tell all threads to exit 
      ReleaseMutex(Mutex); 
      return 2; // Exit with Error 
     } 
     int out = buffer.front(); 
     buffer.erase(buffer.begin()); // Remove item from the list 
     cout << "The Consumer ate: " << out << endl; 
     ReleaseMutex(Mutex); // Could be LeaveCriticalSection() instead 
     ReleaseSemaphore(Empty, 1, NULL); // 1 item removed, update semaphore 
    } 
    cout << "\nThe CONSUMER consumed " << nItems << " items." << endl; 
    return 0; // OK 
} 

Notes:

  • La variable bExit est globale et accessible/modifié par plus Comme cela est toujours fait dans une section critique (verrou mutex), il n'est pas nécessaire d'en utiliser un autre, ou un accès aux variables interlocké.
  • Les messages de diagnostic (par exemple cout << "The Consumer ate: " << out << endl;), ou tout autre type de "traitement" de ces données (ou "travail" sur eux) aurait pu être placé après libérant les objets. Ce serait plus efficace, libérant les objets plus tôt à l'autre thread (s). Je l'ai fait de cette façon pour mieux illustrer la séquence des événements dans vos tests.
  • Si vous définissez MAX_BUF sur 1, les nombres doivent être générés/imprimés un à la fois, sinon ils ne peuvent pas être déterminés, mais les éléments produits moins les éléments consommés ne doivent bien sûr jamais dépasser la taille de la mémoire tampon.