Recherche…


Introduction

L'une des choses les plus difficiles à faire en C et C ++ est la gestion des ressources. Heureusement, en C ++, nous avons plusieurs façons de concevoir la gestion des ressources dans nos programmes. Cet article espère expliquer certains des idiomes et méthodes utilisés pour gérer les ressources allouées.

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
}
C ++ 11

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.

C ++ 11

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
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow