#+TITLE : Prise de notes TME 4I400 PSCR
Yann Thierry-Mieg ([email protected]) 4I400
Non applicable, on ne se sert pas d’Eclipse.
On fait un hello world :
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World !" << endl;
return 0;
}
Non applicable, on ne se sert pas d’Eclipse.
Afficher le contenu d’un tableau créé :
#include <iostream>
using namespace std;
int main()
{
int tab[10];
for (int i = 0; i < 10; ++i) {
tab[i] = i;
cout << i << " ";
}
cout << endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int tab[10];
for (int i = 0; i < 10; ++i) {
tab[i] = i;
}
for (size_t i=9; i > 0 ; i--) {
if (tab[i] - tab[i-1] != 1) {
cout << "probleme !";
}
}
return 0;
}
On est censé découvrir le debugger intégré.
On se servira de gdb : compiler avec le flag -g, mettre le breakpoint à la bonne ligne.
On se sert de gdb pour regarder ce qu’il se passe :
Au breakpoint :
(gdb) print i $1 = 0 (gdb) print tab $2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Un cycle de boucle plus tard :
(gdb) print i $3 = 18446744073709551615
En effet : size_t correspond à l’entier non-signé qu’on peut écrire sur un mot mémoire de la machine (8 octets), donc unsigned long int.
Quand i passe en dessous de 0 (car la condition d’arrêt le permet), il est interprété comme le plus grand nombre écrivable sur 8 octets, soit 2^64 (on imagine on a vérifié 18446744073709551615)
Il faut et il suffit d’écrire correctement la condition d’arrêt, pour ne pas permettre i de passer en-dessous de 0 :
i > 0 sur la condition d’arrêt.
Je ne sais pas ce qu’on a oublié de faire de mal, mais on n’a pas de possibilité de fuite mémoire d’après valgrind.
Débogage
#ifndef SRC_LIST_H_
#define SRC_LIST_H_
#include <cstddef>
#include <string>
#include <ostream>
namespace pr {
class Chainon {
public :
std::string data;
Chainon * next;
Chainon (const std::string & data, Chainon * next=nullptr);
size_t length() ;
void print (std::ostream & os) const;
};
class List {
public:
Chainon * tete;
List(): tete(nullptr) {}
~List() {
for (Chainon * c = tete ; c ; ) {
Chainon * tmp = c->next;
delete c;
c = tmp;
}
}
const std::string & operator[] (size_t index) const ;
void push_back (const std::string& val) ;
void push_front (const std::string& val) {
tete = new Chainon(val,tete);
}
bool empty() ;
size_t size() const ;
};
std::ostream & operator<< (std::ostream & os, const List & vec) ;
} /* namespace pr */
#endif /* SRC_LIST_H_ */
On s’appuiera sur la documentation de référence du langage.
Le code du parseur :
#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>
int main ()
{
using namespace std;
using namespace std::chrono;
ifstream input = ifstream("./WarAndPeace.txt");
auto start = steady_clock::now();
cout << "Parsing War and Peace" << endl;
size_t nombre_lu = 0;
// prochain mot lu
string word;
// une regex qui reconnait les caractères anormaux (négation des lettres)
regex re( R"([^a-zA-Z])");
while (input >> word) {
// élimine la ponctuation et les caractères spéciaux
word = regex_replace ( word, re, "");
// passe en lowercase
transform(word.begin(),word.end(),word.begin(),::tolower);
// word est maintenant "tout propre"
if (nombre_lu % 100 == 0)
// on affiche un mot "propre" sur 100
cout << nombre_lu << ": "<< word << endl;
nombre_lu++;
}
input.close();
cout << "Finished Parsing War and Peace" << endl;
auto end = steady_clock::now();
cout << "Parsing took "
<< duration_cast<milliseconds>(end - start).count()
<< "ms.\n";
cout << "Found a total of " << nombre_lu << " words." << endl;
return 0;
}
Finished Parsing War and Peace Parsing took 1522ms. Found a total of 566193 words.
#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>
#include <vector>
int main ()
{
using namespace std;
using namespace std::chrono;
ifstream input = ifstream("./WarAndPeace.txt");
auto start = steady_clock::now();
cout << "Parsing War and Peace" << endl;
size_t nombre_lu = 0;
// prochain mot lu
string word;
// une regex qui reconnait les caractères anormaux (négation des lettres)
regex re( R"([^a-zA-Z])");
// On met ici le vecteur qui stockera les mots
vector <string> unique_words;
while (input >> word) {
// élimine la ponctuation et les caractères spéciaux
word = regex_replace ( word, re, "");
// passe en lowercase
transform(word.begin(),word.end(),word.begin(),::tolower);
// word est maintenant "tout propre"
// on affiche un mot "propre" sur 100
if (nombre_lu % 100 == 0) cout << nombre_lu << ": "<< word << endl;
nombre_lu++;
bool trouve = false;
for (int i = 0 ; i < unique_words.size(); i++) {
if (unique_words[i] == word) {
trouve = true;
break;
}
}
if (trouve == false) {
unique_words.push_back(word);
}
}
input.close();
cout << "Finished Parsing War and Peace" << endl;
auto end = steady_clock::now();
cout << "Parsing took "
<< duration_cast<milliseconds>(end - start).count()
<< "ms.\n";
cout << "Found a total of " << nombre_lu << " words." << endl;
cout << "Found a total of " << unique_words.size() << " unique words." << endl;
return 0;
}
Sans le break dans le if, en vérifiant encore après avoir trouvé Finished Parsing War and Peace Parsing took 71808ms. (71 secondes, sur le NFS) Found a total of 566193 words. Found a total of 20333 unique words.
Avec le break. Finished Parsing War and Peace Parsing took 11318ms. (11 secondes, sur le NFS) Found a total of 566193 words. Found a total of 20333 unique words.
#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>
#include <vector>
#include <utility>
using namespace std;
using namespace std::chrono;
int occurences_mot(const string mot, vector <pair<string,int>> tab_mot);
int main ()
{
ifstream input = ifstream("./WarAndPeace.txt");
auto start = steady_clock::now();
cout << "Parsing War and Peace" << endl;
size_t nombre_lu = 0;
// prochain mot lu
string word;
// une regex qui reconnait les caractères anormaux (négation des lettres)
regex re( R"([^a-zA-Z])");
// On met ici le vecteur qui stockera les mots
vector <pair <string,int>> unique_words_and_count;
while (input >> word) {
// élimine la ponctuation et les caractères spéciaux
word = regex_replace ( word, re, "");
// passe en lowercase
transform(word.begin(),word.end(),word.begin(),::tolower);
// word est maintenant "tout propre"
// on affiche un mot "propre" sur 100
if (nombre_lu % 100 == 0) cout << nombre_lu << ": "<< word << endl;
nombre_lu++;
bool trouve = false;
for (int i = 0 ; i < unique_words_and_count.size(); i++) {
if (unique_words_and_count[i].first == word) {
trouve = true;
unique_words_and_count[i].second++;
break;
}
}
if (trouve == false) {
unique_words_and_count.emplace_back(word,1);
}
}
input.close();
cout << "Finished Parsing War and Peace" << endl;
auto end = steady_clock::now();
cout << "Parsing took "
<< duration_cast<milliseconds>(end - start).count()
<< "ms.\n";
cout << "Found a total of " << nombre_lu << " words." << endl;
cout << "Found a total of " << unique_words_and_count.size() << " unique words." << endl;
cout << "Found a total of " << occurences_mot("war", unique_words_and_count) << " occurrences of the word war." << endl;
cout << "Found a total of " << occurences_mot("peace", unique_words_and_count) << " occurrences of the word peace." << endl;
cout << "Found a total of " << occurences_mot("napoleon", unique_words_and_count) << " occurrences of the word napoleon." << endl;
cout << "Found a total of " << occurences_mot("pierre", unique_words_and_count) << " occurrences of the word pierre." << endl;
return 0;
}
int occurences_mot(const string mot, const vector <pair<string,int>> tab_mot)
{
for (int i = 0; i < tab_mot.size(); ++i) {
if (mot == tab_mot[i].first) {
return tab_mot[i].second;
}
}
return 0;
}
Finished Parsing War and Peace Parsing took 11661ms. Found a total of 566193 words. Found a total of 20333 unique words. Found a total of 298 occurrences of the word war. Found a total of 114 occurrences of the word peace. Found a total of 475 occurrences of the word napoleon. Found a total of 1784 occurrences of the word pierre.
Pour chacun des n mots du texte, on a besoin de faire [la suite manque]
(Version Thierry-Mieg, au tableau)
#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>
using namespace std;
template <typename K, typename V>
class HashMap {
struct Entry {
const K key;
V value;
Entry(const K& key, const V& value) : key(key), value(value) {}
};
std::vector <std::forward_list <Entry> > buckets;
V* get(const K& key) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
for (Entry& ent : buckets[h]) {
if (ent.key == key) {
return & ent.value;
}
}
return nullptr;
}
};
int main ()
{
using namespace std;
using namespace std::chrono;
ifstream input = ifstream("./WarAndPeace.txt");
auto start = steady_clock::now();
cout << "Parsing War and Peace" << endl;
size_t nombre_lu = 0;
// prochain mot lu
string word;
// une regex qui reconnait les caractères anormaux (négation des lettres)
regex re( R"([^a-zA-Z])");
while (input >> word) {
// élimine la ponctuation et les caractères spéciaux
word = regex_replace ( word, re, "");
// passe en lowercase
transform(word.begin(),word.end(),word.begin(),::tolower);
// word est maintenant "tout propre"
if (nombre_lu % 100 == 0)
// on affiche un mot "propre" sur 100
cout << nombre_lu << ": "<< word << endl;
nombre_lu++;
}
input.close();
cout << "Finished Parsing War and Peace" << endl;
auto end = steady_clock::now();
cout << "Parsing took "
<< duration_cast<milliseconds>(end - start).count()
<< "ms.\n";
cout << "Found a total of " << nombre_lu << " words." << endl;
return 0;
}
(Version Agon-Rambosson)
#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>
#include <vector>
#include <utility>
#include <forward_list>
using namespace std;
template <typename K, typename V>
class HashMap {
struct Entry {
const K key;
V value;
Entry(const K& key, const V& value) : key(key), value(value) {}
};
std::vector <std::forward_list<Entry>> buckets;
size_t nb_stored_values;
public:
HashMap(size_t size) {
buckets.resize(size);
nb_stored_values = 0;
}
HashMap() {
buckets.resize(256);
nb_stored_values = 0;
}
size_t nb_buckets() const {
return buckets.size();
}
size_t size() const {
return nb_stored_values;
}
void grow() {
size_t former_size = buckets.size();
HashMap nouvelle_map(2 * former_size);
K temp_key;
V temp_value;
size_t temp_nb_values = 0;
for (int i = 0; i < former_size ; i++) {
while (buckets[i].empty() != 1) {
temp_key = buckets[i].front().key;
temp_value = buckets[i].front().value;
buckets[i].pop_front();
nb_stored_values--;
nouvelle_map.put(temp_key, temp_value);
temp_nb_values++;
}
}
buckets.swap(nouvelle_map.buckets);
this->nb_stored_values = temp_nb_values;
nouvelle_map.buckets.clear();
}
V* get(const K& key) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
for (Entry& ent : buckets[h]) {
if (ent.key == key) {
return &ent.value;
}
}
return nullptr;
}
bool put(const K& key, const V& value) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
for (Entry& ent : buckets[h]) {
if (ent.key == key) {
ent.value = value;
return true;
}
}
if (((this->size() + 0.0) / (this->nb_buckets() + 0.0)) > 0.8) {
this->grow();
}
buckets[h].push_front(Entry(key, value));
this->nb_stored_values++;
return false;
}
bool increment(const K& key) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
for (Entry& ent : buckets[h]) {
if (ent.key == key) {
ent.value++;
return true;
}
}
if (((this->size() + 0.0) / (this->nb_buckets() + 0.0)) > 0.8) {
this->grow();
}
buckets[h].push_front(Entry(key, 1));
this->nb_stored_values++;
return false;
}
bool del(const K& key) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
auto prev = buckets[h].before_begin();
for (auto it = buckets[h].before_begin();
it != buckets[h].end();) {
prev = it;
if ((++it)->key == key) {
buckets[h].erase_after(prev);
nb_stored_values--;
return true;
}
}
return false;
}
};
int main ()
{
using namespace std;
using namespace std::chrono;
ifstream input = ifstream("./WarAndPeace.txt");
auto start = steady_clock::now();
cout << "Parsing War and Peace" << endl;
size_t nombre_lu = 0;
// prochain mot lu
string word;
// une regex qui reconnait les caractères anormaux (négation des lettres)
regex re( R"([^a-zA-Z])");
// On créé la structure qui accueillera les données
HashMap<string,int> table;
while (input >> word) {
// élimine la ponctuation et les caractères spéciaux
word = regex_replace ( word, re, "");
// passe en lowercase
transform(word.begin(),word.end(),word.begin(),::tolower);
// word est maintenant "tout propre"
// on affiche un mot "propre" sur 100
if (nombre_lu % 100 == 0) cout << nombre_lu << ": "<< word << endl;
nombre_lu++;
table.increment(word);
}
cout << "Finished Parsing War and Peace" << endl;
auto end = steady_clock::now();
cout << "Parsing took "
<< duration_cast<milliseconds>(end - start).count()
<< "ms.\n";
cout << "Found a total of " << nombre_lu << " words." << endl;
cout << "Found a total of " << table.size() << " unique words." << endl;
cout << "We need " << table.nb_buckets() << " buckets" << endl;
cout << "On trouve " << *(table.get("pierre")) << " occurences du mot pierre" << endl;
return 0;
}
Finished Parsing War and Peace Parsing took 3225ms. Found a total of 566193 words. Found a total of 20333 unique words. We need 32768 buckets
On peut déjà remarquer qu’on a bien les mêmes valeurs qu’avec le vecteur.
Le parsing n’a pris que 3 secondes, alors même qu’on est sur un ordinateur bien moins puissant (à tester en condition similaire).
Le nombre de buckets est bien tel que le nombre de valeur uniques / le nombre de buckets est en deçà de 0.8. On a choisi de réallouer le double à chaque itération.
De cette manière, le coût des réallocations est en log_2(n).
#include <iostream>
#include <fstream>
#include <regex>
#include <chrono>
#include <vector>
#include <utility>
#include <forward_list>
using namespace std;
template <typename K, typename V>
class HashMap {
public:
struct Entry {
const K key;
V value;
Entry(const K& key, const V& value) : key(key), value(value) {}
};
private:
typedef std::vector<std::forward_list<Entry>> buckets_t;
buckets_t buckets;
size_t nb_stored_values;
public:
HashMap(size_t size) {
buckets.resize(size);
nb_stored_values = 0;
}
HashMap() {
buckets.resize(256);
nb_stored_values = 0;
}
size_t nb_buckets() const {
return buckets.size();
}
size_t size() const {
return nb_stored_values;
}
void grow() {
size_t former_size = buckets.size();
HashMap nouvelle_map(2 * former_size);
K temp_key;
V temp_value;
size_t temp_nb_values = 0;
for (size_t i = 0; i < former_size ; i++) {
while (buckets[i].empty() != 1) {
temp_key = buckets[i].front().key;
temp_value = buckets[i].front().value;
buckets[i].pop_front();
nb_stored_values--;
nouvelle_map.put(temp_key, temp_value);
temp_nb_values++;
}
}
buckets.swap(nouvelle_map.buckets);
this->nb_stored_values = temp_nb_values;
nouvelle_map.buckets.clear();
}
V* get(const K& key) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
for (Entry& ent : buckets[h]) {
if (ent.key == key) {
return &ent.value;
}
}
return nullptr;
}
bool put(const K& key, const V& value) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
for (Entry& ent : buckets[h]) {
if (ent.key == key) {
ent.value = value;
return true;
}
}
if (((this->size() + 0.0) / (this->nb_buckets() + 0.0)) > 0.8) {
this->grow();
}
buckets[h].push_front(Entry(key, value));
this->nb_stored_values++;
return false;
}
bool increment(const K& key) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
for (Entry& ent : buckets[h]) {
if (ent.key == key) {
ent.value++;
return true;
}
}
if (((this->size() + 0.0) / (this->nb_buckets() + 0.0)) > 0.8) {
this->grow();
}
buckets[h].push_front(Entry(key, 1));
this->nb_stored_values++;
return false;
}
bool del(const K& key) {
size_t h = std::hash <K>()(key);
h = h % buckets.size();
auto prev = buckets[h].before_begin();
for (auto it = buckets[h].before_begin();
it != buckets[h].end();) {
prev = it;
if ((++it)->key == key) {
buckets[h].erase_after(prev);
nb_stored_values--;
return true;
}
}
return false;
}
struct iterator {
typename std::vector<std::forward_list<Entry>>::iterator vit;
typename std::forward_list<Entry>::iterator lit;
buckets_t *buckets;
iterator (buckets_t &buckets) : buckets(&buckets) {}
iterator &operator++() {
++lit;
if (lit != vit->begin()) return *this;
while (++vit -> empty() && vit != buckets->end()) {
if (vit != buckets->end()) {
lit = vit->begin();
}
}
return *this;
}
bool operator!=(iterator & other) {
return ((vit != other.vit) || (lit != other.lit));
}
};
iterator begin() {
auto temp = iterator(&buckets);
for (size_t i = 0; i < buckets.size() ; ++i) {
if (buckets[i].empty()) {
temp.vit = buckets.begin() + i;
temp.lit = temp.vit->begin();
break;
}
}
return temp;
}
iterator end() {
auto temp = iterator(&buckets);
temp.lit = buckets[buckets.size() - 1].end();
temp.vit = buckets.end();
return temp;
}
size_t count(iterator begin, iterator end) {
size_t compteur = 0;
for (auto it = begin; it != end; ++it, ++compteur);
return compteur;
}
};
int main ()
{
using namespace std;
using namespace std::chrono;
ifstream input = ifstream("./WarAndPeace.txt");
auto start = steady_clock::now();
cout << "Parsing War and Peace" << endl;
size_t nombre_lu = 0;
// prochain mot lu
string word;
// une regex qui reconnait les caractères anormaux (négation des lettres)
regex re( R"([^a-zA-Z])");
// On créé la structure qui accueillera les données
HashMap<string,int> table;
while (input >> word) {
// élimine la ponctuation et les caractères spéciaux
word = regex_replace ( word, re, "");
// passe en lowercase
transform(word.begin(),word.end(),word.begin(),::tolower);
// word est maintenant "tout propre"
// on affiche un mot "propre" sur 100
if (nombre_lu % 100 == 0) cout << nombre_lu << ": "<< word << endl;
nombre_lu++;
table.increment(word);
}
cout << "Finished Parsing War and Peace" << endl;
auto end = steady_clock::now();
cout << "Parsing took "
<< duration_cast<milliseconds>(end - start).count()
<< "ms.\n";
cout << "Found a total of " << nombre_lu << " words." << endl;
cout << "Found a total of " << table.size() << " unique words." << endl;
cout << "We need " << table.nb_buckets() << " buckets" << endl;
cout << "On trouve " << *(table.get("pierre")) << " occurences du mot pierre" << endl;
cout << "Il y a " << table.count(table.begin(), table.end()) << " mots" << endl;
return 0;
}
On rappelle toutes les classes sous-jacentes :
Le fichier d’en-tête de la classe Compte :
#pragma once
#include <thread>
#include <mutex>
namespace pr {
class Compte {
mutable std::mutex m;
int solde;
public :
Compte(int solde=0):solde(solde) {}
Compte(const Compte & other);
void crediter (unsigned int val) ;
bool debiter (unsigned int val) ;
int getSolde() const;
mutex &getMutex();
};
}
L’implémentation des méthodes :
#include "Compte.h"
using namespace std;
namespace pr {
void Compte::crediter (unsigned int val) {
unique_lock<mutex> g(m);
solde+=val ;
}
bool Compte::debiter (unsigned int val) {
unique_lock<mutex> g(m);
bool doit = solde >= val;
if (doit) {
solde-=val ;
}
return doit;
}
int Compte::getSolde() const {
unique_lock<mutex> g(m);
return solde;
}
// NB : vector exige Copyable, mais mutex ne l'est pas...
Compte::Compte(const Compte & other) {
other.m.lock();
solde = other.solde;
other.m.unlock();
}
Compte::getmutex() {
}
}
On rajoute la classe Banque :
#pragma once
#include "Compte.h"
#include <vector>
namespace pr {
class Banque {
typedef std::vector<Compte> comptes_t;
comptes_t comptes;
public :
Banque (size_t ncomptes, size_t solde) : comptes (ncomptes, Compte(solde)){
}
void transfert(size_t deb, size_t cred, unsigned int val) ;
size_t size() const ;
bool comptabiliser (int attendu) const ;
int get_solde(size_t compte) ;
};
}
L’implémentation des méthodes :
#include "Banque.h"
#include <iostream>
using namespace std;
namespace pr {
void Banque::transfert(size_t deb, size_t cred, unsigned int val) {
Compte & debiteur = comptes[deb];
Compte & crediteur = comptes[cred];
if (debiteur.debiter(val)) {
crediteur.crediter(val);
}
}
size_t Banque::size() const {
return comptes.size();
}
bool Banque::comptabiliser(int attendu) const {
int bilan = 0;
int id = 0;
for (const auto & compte : comptes) {
if (compte.getSolde() < 0) {
cout << "Compte " << id << " en négatif : " << compte.getSolde() << endl;
}
bilan += compte.getSolde();
id++;
}
if (bilan != attendu) {
cout << "Bilan comptable faux : attendu " << attendu << " obtenu : " << bilan << endl;
}
return bilan == attendu;
}
int Banque::get_solde(size_t compte) {
return comptes[compte].getSolde();
}
}
#include <thread>
#include <iostream>
#include "Banque.h"
using namespace std;
const int NB_THREAD = 20;
const int K = 100;
const int SOLDEINITIAL = 1000;
// En fait, il fallait bien écrire la fonction ici :
void transferts_aleatoires(pr::Banque *banque) {
int compteur = 1000;
for (; compteur > 0; --compteur) {
auto i = ::rand() % banque->size();
auto j = ::rand() % banque->size();
// On part du principe qu'on peut se faire des transferts à soi-même
auto m = ::rand() % 100;
// cout << m << endl;
banque->transfert(i,j,m);
// On s'endort pendant une durée aléatoire entre 0 et 200ms
auto r = ::rand() % 20;
std::this_thread::sleep_for(std::chrono::milliseconds(r));
}
}
int bilan_naif(pr::Banque *banque)
{
int bilan = 0;
for (int i = 0; i < K ; ++i) {
bilan += banque->get_solde(i);
}
return bilan;
}
int main()
{
// On fixe la graine
::srand(::time(nullptr));
// On créé une instance de banque
pr::Banque LBP(K, SOLDEINITIAL);
// On vérifie que le comptable est content
// cout << LBP.comptabiliser(K * SOLDEINITIAL) << endl;
cout << bilan_naif(&LBP) << endl;
// On vérifie qu'un compte random est bien à la bonne valeur
cout << LBP.get_solde(12) << endl;
vector<thread> threads;
// TODO : creer des threads qui font ce qui est demandé
threads.reserve(NB_THREAD);
for (int i = 0; i < NB_THREAD; ++i) {
threads.push_back(thread(transferts_aleatoires, &LBP));
}
// Threads créés
for (auto & t : threads) {
t.join();
}
// Est-ce que le comptable est content ?
// cout << LBP.comptabiliser(K * SOLDEINITIAL) << endl;
cout << bilan_naif(&LBP) << endl;
// Est-ce que notre fonction a vraiment fait qqch ?
cout << LBP.get_solde(12) << endl;
cout << LBP.get_solde(37) << endl;
cout << LBP.get_solde(15) << endl;
cout << LBP.get_solde(87) << endl;
// TODO : tester solde = NB_THREAD * JP
return 0;
}
Pour compiler tout ça, on écrit un makefile :
tme4: Banque.cpp Compte.cpp main.cpp
g++ -std=c++1y -O0 -g3 -Wall -c -lpthread -o ./Compte.o ./Compte.cpp
g++ -std=c++1y -O0 -g3 -Wall -c -lpthread -o ./Banque.o ./Banque.cpp
g++ -std=c++1y -O0 -g3 -Wall -c -lpthread -o ./tme4.o ./main.cpp
g++ -lpthread -o ./tme4 ./Compte.o ./Banque.o ./tme4.o
Déjà, on arrive à compiler avec les codes donnés plus haut.
On n’a pas de problème pour le moment, avec K = 100, 10 threads, et 1000 itérations dans le thread.
On passe bien l’instance de la banque par pointeur, et on vérifie bien que les comptes sont modifiés.
Comment forcer une erreur ?
On fait un truc style d.lock, c.lock, section critique
recursive_mutex doit être utilisé (on se rebloque entre le premier verrou qu’on vient de définir, et le deuxième dans le corps des méthodes)
Ensuite :
Deadlock introduit par interblocage.
Il faut bien ordonner les verrous : on peut demander au système de faire ça, ou on peut le faire à la main.
Sinon Big Fat Lock : on met un mutex sur la banque (mais l’intérêt est nul)
Pour le comptable :
v0 la fonction comptabiliser, lock tous les comptes.
Lock progressif : lock dans l’ordre d’indice.
[à reprendre quand on a le temps]