C++
La gestion des ressources
Recherche…
Introduction
L'acquisition de ressources est une initialisation
L'acquisition de ressources est l'initialisation (RAII) est un idiome commun dans la gestion des ressources. Dans le cas de la mémoire dynamique, il utilise des pointeurs intelligents pour effectuer la gestion des ressources. Lors de l'utilisation de RAII, une ressource acquise est immédiatement attribuée à un pointeur intelligent ou à un gestionnaire de ressources équivalent. La ressource est uniquement accessible via ce gestionnaire, de sorte que le gestionnaire peut suivre les différentes opérations. Par exemple, std::auto_ptr
libère automatiquement la ressource correspondante lorsqu'elle est hors de portée ou est supprimée d'une autre manière.
#include <memory>
#include <iostream>
using namespace std;
int main() {
{
auto_ptr ap(new int(5)); // dynamic memory is the resource
cout << *ap << endl; // prints 5
} // auto_ptr is destroyed, its resource is automatically freed
}
Le problème principal de std::auto_ptr
est qu'il ne peut pas être copié sans transférer la propriété:
#include <memory>
#include <iostream>
using namespace std;
int main() {
auto_ptr ap1(new int(5));
cout << *ap1 << endl; // prints 5
auto_ptr ap2(ap1); // copy ap2 from ap1; ownership now transfers to ap2
cout << *ap2 << endl; // prints 5
cout << ap1 == nullptr << endl; // prints 1; ap1 has lost ownership of resource
}
À cause de cette sémantique de copie bizarre, std::auto_ptr
ne peut pas être utilisé, entre autres, dans des conteneurs. La raison en est que cela empêche la suppression de la mémoire deux fois: s'il y a deux auto_ptrs
avec la même ressource, ils essaient tous deux de le libérer lorsqu'ils sont détruits. Libérer une ressource déjà libérée peut généralement poser problème, il est donc important de la prévenir. Cependant, std::shared_ptr
a une méthode pour éviter cela tout en ne transférant pas la propriété lors de la copie:
#include <memory>
#include <iostream>
using namespace std;
int main() {
shared_ptr sp2;
{
shared_ptr sp1(new int(5)); // give ownership to sp1
cout << *sp1 << endl; // prints 5
sp2 = sp1; // copy sp2 from sp1; both have ownership of resource
cout << *sp1 << endl; // prints 5
cout << *sp2 << endl; // prints 5
} // sp1 goes out of scope and is destroyed; sp2 has sole ownership of resource
cout << *sp2 << endl;
} // sp2 goes out of scope; nothing has ownership, so resource is freed
Mutexes et sécurité des fils
Des problèmes peuvent survenir lorsque plusieurs threads tentent d'accéder à une ressource. Pour un exemple simple, supposons que nous ayons un thread qui en ajoute un à une variable. Pour cela, il faut d'abord lire la variable, en ajouter une, puis la stocker. Supposons que nous initialisons cette variable à 1, puis créons deux instances de ce thread. Une fois les deux threads terminés, l'intuition suggère que cette variable ait une valeur de 3. Cependant, le tableau ci-dessous illustre ce qui pourrait mal tourner:
Fil 1 | Fil 2 | |
---|---|---|
Étape 1 | Lire 1 de la variable | |
Étape 2 | Lire 1 de la variable | |
Étape 3 | Ajouter 1 plus 1 pour obtenir 2 | |
Étape 4 | Ajouter 1 plus 1 pour obtenir 2 | |
Étape 5 | Stocker 2 en variable | |
Étape 6 | Stocker 2 en variable |
Comme vous pouvez le voir, à la fin de l'opération, 2 est dans la variable, au lieu de 3. La raison en est que Thread 2 lit la variable avant que Thread 1 ne soit terminé. La solution? Mutexes
Un mutex (portemanteau mut ex uel clusion) est un objet de gestion des ressources conçu pour résoudre ce type de problème. Lorsqu'un thread veut accéder à une ressource, il "acquiert" le mutex de la ressource. Une fois l'accès à la ressource terminé, le thread "libère" le mutex. Tant que le mutex est acquis, tous les appels pour acquérir le mutex ne reviendront pas tant que le mutex n'est pas libéré. Pour mieux comprendre cela, pensez à un mutex comme une file d'attente au supermarché: les threads s'alignent en essayant d'acquérir le mutex, puis attendent que les threads qui les précèdent finissent, puis utilisent la ressource, puis sortent de ligne en libérant le mutex. Il y aurait un pandémonium complet si tout le monde essayait d'accéder à la ressource immédiatement.
std::mutex
est l'implémentation d'un mutex par C ++ 11.
#include <thread>
#include <mutex>
#include <iostream>
using namespace std;
void add_1(int& i, const mutex& m) { // function to be run in thread
m.lock();
i += 1;
m.unlock();
}
int main() {
int var = 1;
mutex m;
cout << var << endl; // prints 1
thread t1(add_1, var, m); // create thread with arguments
thread t2(add_1, var, m); // create another thread
t1.join(); t2.join(); // wait for both threads to finish
cout << var << endl; // prints 3
}