2015-04-28 1 views
3

J'ai commencé à apprendre le multithread et j'ai rencontré des contrats à terme et des promesses de synchronisation des threads sur des ressources partagées. J'ai donc pensé mettre en œuvre un fameux problème de Double Buffering en utilisant Futures and Promises (producteur unique et consommateur unique). La méthodologie de base ce que j'ai pensé est:Mise en œuvre de la double mise en mémoire tampon à l'aide de contrats à terme et de promesses à l'aide de C++ 11

ProducerThread:

loop:  
    locks_buffer1_mutex  
    fills_buffer1 
    unlocks_buffer1_mutex  
    passes number 1 to Consumer thread using promise.setvalue()  
    locks_buffer2_mutex  
    fills_buffer2 
    unlocks_buffer2_mutex  
    passes number 2 to Consumer thread using promise.setvalue() 
back_to_loop 

ConsumerThread:

loop: 
    wait_for_value_from_promise 
    switch 
     case 1: 
     lock_buffer1_mutex 
      process(buffer1) 
     unlock_buffer1_mutex 
     print_values 
     break 
     case 2: 
     lock_buffer2_mutex 
     process(buffer2) 
     unlock_buffer2_mutex 
     print_values 
     break 
back_to_loop 

Voici le code:

#include <iostream> 
#include <thread> 
#include <vector> 
#include <future> 
#include <mutex> 
#include <iterator> 


std::mutex print_mutex; 
std::mutex buffer1_mutex; 
std::mutex buffer2_mutex; 

std::vector<int> buffer1; 
std::vector<int> buffer2; 

bool notify; 


void DataAcquisition(std::promise<int> &p) 
{ 
    std::this_thread::sleep_for(std::chrono::seconds(2)); 
    while(true) 
    { 
     { 
      std::lock_guard<std::mutex> buff1_lock(buffer1_mutex); 
      for(int i=0;i<200;i++) 
      { 
       buffer1.push_back(i); 
      } 
     } 
     p.set_value(1); 
     { 
      std::lock_guard<std::mutex> buff2_lock(buffer2_mutex); 
      for(int i=0;i<200;i++) 
      { 
       buffer2.push_back(199-i); 
      } 
     } 
     p.set_value(2); 
    } 
} 

void DataExtraction(std::future<int> &f) 
{ 
    std::vector<int>::const_iterator first,last; 
    std::vector<int> new_vector; 
    std::ostream_iterator<int> outit(std::cout, " "); 

    while(true) 
    { 
     int i = f.get(); 
     std::cout << "The value of i is :" << i << std::endl; 
     switch(i) 
     { 
      case 1: 
       { 
        std::lock_guard<std::mutex> buff1_lock(buffer1_mutex); 
        first = buffer1.begin(); 
        last = first + 10; 
       } 
       new_vector = std::vector<int>(first,last); 
       { 
        std::lock_guard<std::mutex> print_lock(print_mutex); 
        std::copy(new_vector.begin(),new_vector.end(),outit); 
       } 
       break; 
       case 2: 
       { 
        std::lock_guard<std::mutex> buff2_lock(buffer2_mutex); 
        first = buffer2.begin(); 
        last = first + 10; 
       } 
       new_vector = std::vector<int>(first,last); 
       { 
        std::lock_guard<std::mutex> print_lock(print_mutex); 
        std::copy(new_vector.begin(),new_vector.end(),outit); 
       } 
       break; 
      } 
    } 
} 

int main() 
{ 
    std::promise<int> p; 
    std::future<int> f = p.get_future(); 


    std::thread thread1(DataAcquisition,std::ref(p)); 
    std::thread thread2(DataExtraction,std::ref(f)); 

    thread1.join(); 
    thread2.join(); 

    return 0; 
} 

Quand j'exécuter ce code, je suis tombé par son problème giagntic, que je suis ignorant totalement

terminate called after throwing an instance of 'std::future_error' terminate called recursively 
    what(): 0 1 2 3 4 5 6 7 8 9 Promise already satisfied 
Press <RETURN> to close the window 

Je googled sur cette erreur, il est suggéré de relier commutateur -lpthread pendant la liaison et la compilation. mais n'a pas pu résoudre le problème.

S'il vous plaît aidez-moi, où vais-je mal ..

+0

Pour ce que vous essayez de faire, un atome ne suffirait-il pas pour indiquer le dernier tampon terminé? Notez qu'il n'y a aucune garantie que le «consommateur» verra toutes les images produites par le «producteur». C'est facile à voir intuitivement si vous imaginez que les «valeurs d'impression» sont lentes. Le consommateur ne détient aucun verrou pendant les 'valeurs d'impression' et le 'Producteur' pourrait faire des cliquetis en produisant un nombre quelconque de cadres et en les écrivant. Même si cela ne vous dérange pas de 'laisser tomber les cadres' une coopération plus subtile signifierait que vous ne gaspillez pas les cycles de CPU produisant des résultats que personne ne voit jamais. – Persixty

+0

Par un argument similaire 'Consumer' pourrait traiter le même cadre deux fois. – Persixty

Répondre

3

Vous ne pouvez pas appeler set_value pour une promise plus qu'une fois, ce qui est illustré par le code suivant:

#include <future> 

int main() { 
    std::promise<int> p; 
    p.set_value(1); 
    p.set_value(2); // Promise already satisfied 
} 

Vous devez chercher une autre approche. Par exemple, vous pouvez utiliser deux std::condition_variable s - les définir dans le producteur, et les attendre dans le consommateur.

+0

Salut Anton, Vous voulez dire que nous ne pouvons pas appeler set_value sur une promesse plus d'une fois dans l'exécution entière du problème ... ?? La suggestion que vous avez faite concernant les tampons de lecture "à tour de rôle", je n'ai pas pu obtenir exactement ce que vous me suggérez de faire. – jeldikk

+1

@ user1511151 Oui, une fois la valeur définie, vous ne pouvez plus le faire. En ce qui concerne "à son tour" il semble que cela ne fonctionnera pas facilement, vous pouvez utiliser des variables de condition, voir ma mise à jour. –

+0

À droite, une promesse est juste une valeur + temps, une fois la valeur est là, il est impossible de la redéfinir - c'est comme essayer de définir 5 fois deux fois. –