2013-07-02 12 views
8

Obecnie pracuję nad problemem symulującym rozszerzony model Producent-Pracownik. W tym problemie są dostępne 3 pracowników i 3 narzędzia, a dla pracowników do pracy potrzebne są 2 narzędzia (i materiały, ale te są nieistotne). Jeśli w skarbcu są> = 2 narzędzia, robotnik wybierze 2. Inaczej będą czekać na zmienną warunku, która będzie sygnalizowana, gdy> = 2.C++ 11 Wątek: wiele wątków oczekujących na zmienną warunku

Jest to w porządku z 2 pracownikami: jeden będzie pracował, a następnie zwróci narzędzia do skarbca, a drugi pracownik oczekujący zostanie przebudzony i zabierze 2 narzędzia. Problem polega na tym, że przy 3 pracownikach zawsze będzie ktoś głodny, aby zdobyć narzędzia.

Po kilku testach zauważyłem, że wątki oczekujące na zmienną warunku mają strukturę stosu. Czy mimo to istnieje możliwość umieszczenia go w kolejce? (1 czeka, 2 czeka i czeka 3), gdy 1 się obudzi i chce wykonać drugą, musi poczekać za 2 i 3.)

Oto jedno przykładowe wyjście. Kod jest za długi, więc opublikuję go, jeśli jest to naprawdę konieczne. Istnieją 3 wątki robocze i 1 mutex narzędzia. Kto głoduje, różni się co drugi bieg.

1 Tools taken. Remaining: 1 
2 Waiting on tools... 
3 Waiting on tools... 
1 Operator Product made. Tools returned. Tools now:3 
3 Tools taken. Remaining: 1 
1 Waiting on tools... 
3 Materials returned for switch. 
3 Operator Product made. Tools returned. Tools now:3 
1 Tools taken. Remaining: 1 
3 Waiting on tools... 
1 Materials returned for switch. 
1 Operator Product made. Tools returned. Tools now:3 
3 Tools taken. Remaining: 1 
1 Waiting on tools... 
3 Materials returned for switch. 
3 Operator Product made. Tools returned. Tools now:3 
1 Tools taken. Remaining: 1 
3 Waiting on tools... 
1 Materials returned for switch. 
1 Operator Product made. Tools returned. Tools now:3 
3 Tools taken. Remaining: 1 
1 Waiting on tools... 
3 Materials returned for switch. 
3 Operator Product made. Tools returned. Tools now:3 
1 Tools taken. Remaining: 1 
3 Waiting on tools... 
1 Materials returned for switch. 
... 

(Jak widać 2 nigdy nie dostaje narzędzia ...)

aktualizacja: 2013/07/05 dodałem trochę kodu.

int tools = 3; //global 
string last; //current last product on output buffer 
mutex toolsMutex; 
mutex matSearchMutex; 

int main(){ 
//Initializing Producers 
    Producer prod1(1); 
    Producer prod2(2); 
     Producer prod3(3); 



    thread p1(processor,1); 
    thread p2(processor,2); 
    thread p3(processor,3); 

    p1.detach(); 
    p2.detach(); 
    p3.detach(); 

    while(true){//forever running 

    } 

    return 0; 
} 

Procesor:

//Processor method 
void processor(int i){ 
    srand(time(NULL)); 

    while (true){ //forever running 


    bool hasTools = false; 
    bool productMade = false; 
    while (productMade == false){ //while product has yet to be made. 
     //choose what to make... 



     if (hasTools == false){ 
      thread matT(getMaterials,whatToMake); 
      thread toolT(getTools,i); 
      toolT.join();   
      matT.join(); 
      hasTools = true; 
     } 
     else{ //tools acquired but no materials 
      thread matT(getMaterials,whatToMake); 
      matT.join(); 
     } 

     if (recordedLast.compare(last) != 0){ 

      //return materials and acquire new ones the next run 

      continue; 
     } 
     else { 
      makeProduct(whatToMake); 
      unique_lock<mutex> locker(toolMutex); 
      tools = tools + 2; 
      cout << i << " Operator Product made. Tools returned. Tools now:" << tools << endl; 
      productMade = true; 
      if (tools >=2) toolsCV.notify_one(); 
     } 

    //done processing 

    } 


} 

} 

makeProducts:

void makeProduct(int i){ 
    unique_lock<mutex> mainMatLock(matSearchMutex); 
    // make product according to i 
    this_thread::sleep_for(chrono::milliseconds(rand() % 1000 + 10)); 
} 

getTools:

void getTools(int i){ 
    unique_lock<mutex> locker(toolMutex); 
    if (tools <2){ 
     cout << i << " Waiting on tools..." << endl; 
     toolsCV.wait(locker);} 
    tools = tools - 2;//tools acquired 
    cout << i <<" Tools taken. Remaining: " << tools << endl; 

} 

Dzięki ci, którzy odpowiedzieli. Spróbuję zaimplementować dzisiejszą kolejkę oczekiwania przy użyciu wielu zmiennych warunkowych.

(PS Czy jest jakiś lepszy sposób to zrobić formatowanie kodu tutaj na przepełnienie stosu? Inny niż czterech miejscach ...

+0

Dlaczego nie zamieścisz swojego prawdziwego kodu? Czy próbowałeś nadawania na warstwie zmiennej (przeciw sygnałowi)? Nie określiłeś niczego w swoim środowisku, ale możesz rzucić okiem na http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_08_04_01 –

+1

_ "Czy jest jakiś lepszy sposób na zrobienie kodu formatowanie tutaj w Stack Overflow? "_ Wprowadź kod bez spacji, a następnie podświetl cały blok kodu i naciśnij Ctrl-K lub naciśnij przycisk ** {} ** na pasku narzędzi nad polem edycji –

Odpowiedz

9

std::condition_variable nie określa, który wątek oczekujący jest budzony, gdy zadzwonisz pod numer notify_one. Powinieneś więc napisać kod, który nie dba o to, który wątek jest budzony. Standardowy wzorzec polega na tym, że niezależnie od tego, który wątek jest budzony, ten wątek powinien wykonać pracę, którą należy wykonać.

Jeśli chcesz, aby wątki były budzone w określonej kolejności, użyj innego mechanizmu. Można na przykład mieć oddzielne std::condition_variable dla każdego wątku, a następnie umieścić wątki w kolejce, gdy będą potrzebować narzędzi. Gdy wątek wchodzi w narzędzia, może wtedy zasygnalizować zmienną warunku odpowiadającą wątkowi z przodu kolejki. Wątek zostanie następnie obudzony, a pozostałe pozostaną w stanie uśpienia (modulo pozorne pobudki).

3

Kiedy istnieje kilka wątków oczekujących na warunku, zlecenie w którym są obudzeni (notify_all) lub których jeden jest obudził (notify_one) jest nieokreślona Jeśli potrzebujesz jakiś zamawiania, trzeba użyć notify_all i wdrożyć go samemu można zachować kolejki wątków oczekujących:.. przed czekania (ale po nabyciu muteksu), wciśnij identyfikator wątku na na końcu kolejki. "ten wątek pod numerem z przodu kolejki i niezbędnymi narzędziami dostępnymi". Po uzyskaniu narzędzia usuń identyfikator z przedniej części kolejki i ponownie wywołaj numer notify_all.

0

Jednym z podejść może być użycie w pełni rozwiniętego semafora, który jest współdzielony między wątkami zamiast zmiennej warunkowej. W ten sposób możesz poczekać na określoną liczbę.

#include <mutex> 
#include <thread> 
#include <condition_variable> 

using std::mutex; 
using std::condition_variable; 

class Semaphore 
{ 
public: 
    /** 
    * Construct a counting semaphore with an initial value 
    * @param cnt The value of the initial semaphore count 
    */ 
    Semaphore(unsigned int cnt); 

    /** 
    * acquire a semaphore count 
    * @param numRes The number of count ressources to acquire 
    */ 
    void acquire(unsigned int numRes = 1); 

    /** 
    * try to acquire a semaphore count. 
    * @param numRes The number of count ressources that the method tries to acquire 
    * @return true, if numRes could be aquired 
    *   false, otherwise 
    */ 
    bool tryAcquire(unsigned int numRes = 1); 

    /** 
    * release one semaphore cnt 
    * @param numRes The number of count ressources to release 
    */ 
    void release(unsigned int numRes = 1); 

private: 
    unsigned int cnt; 
    mutex mut; 
    condition_variable cond; 
}; 

Realizacja wygląda następująco:

void Semaphore::acquire(unsigned int numRes) 
{ 
    unique_lock<mutex> lock(mut); 
    while (cnt < numRes) 
    { 
     cond.wait(lock); 
    } 

    cnt-=numRes; 
} 

bool Semaphore::tryAcquire(unsigned int numRes) 
{ 
    unique_lock<mutex> lock(mut); 
    if (cnt>=numRes) 
    { 
     cnt -= numRes; 
     return true; 
    } 
    return false; 
} 

void Semaphore::release(unsigned int numRes) 
{ 
    { 
     unique_lock<mutex> lock(mut); 
     cnt += numRes; 
    } 
    // notify <numRes> waiting entities 
    for (unsigned int i = 0; i<numRes; ++i) 
    { 
     cond.notify_one(); 
    } 
} 
1

Prawdziwym problemem jest to, że jeśli masz wątków roboczych i ograniczoną ilość potrzebnych zasobów, nie powinien dbać który podaje rzeczywiście jest aktywna, należy tylko opieka że praca zostanie wykonana. Jedyna różnica polega na rejestrowaniu. Liczba zdefiniowanych wątków to liczba wątków, które mogą być uruchamiane równolegle, a zasoby ograniczone są do jednego.

Jeśli to nie jest dla ciebie w porządku, musisz podjąć działania samodzielnie, jak wyjaśniono w innych odpowiedziach.

Powiązane problemy