Ricerca…


introduzione

Una delle cose più difficili da fare in C e C ++ è la gestione delle risorse. Fortunatamente, in C ++, abbiamo molti modi per progettare la gestione delle risorse nei nostri programmi. Questo articolo spera di spiegare alcuni degli idiomi e dei metodi usati per gestire le risorse allocate.

L'acquisizione delle risorse è l'inizializzazione

L'acquisizione delle risorse è inizializzazione (RAII) è un idioma comune nella gestione delle risorse. Nel caso della memoria dinamica, utilizza puntatori intelligenti per realizzare la gestione delle risorse. Quando si utilizza RAII, una risorsa acquisita viene immediatamente assegnata a un puntatore intelligente oa un gestore risorse equivalente. La risorsa è accessibile solo attraverso questo gestore, quindi il gestore può tenere traccia di varie operazioni. Ad esempio, std::auto_ptr libera automaticamente la sua risorsa corrispondente quando non rientra nell'ambito o viene cancellata in altro modo.

#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

std::auto_ptr problema principale di std::auto_ptr è che non può essere copiato senza trasferire la proprietà:

#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
}

A causa di questa strana semantica della copia, std::auto_ptr non può essere usato in contenitori, tra le altre cose. La ragione è che impedisce di cancellare la memoria due volte: se ci sono due auto_ptrs con la proprietà della stessa risorsa, entrambi provano a liberarlo quando vengono distrutti. Liberare una risorsa già liberata può generalmente causare problemi, quindi è importante prevenirlo. Tuttavia, std::shared_ptr ha un metodo per evitare ciò mentre non trasferisce la proprietà durante la copia:

#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

Mutex e sicurezza del filo

Possono verificarsi problemi quando più thread tentano di accedere a una risorsa. Per un semplice esempio, supponiamo di avere un thread che aggiunge uno a una variabile. Lo fa leggendo prima la variabile, aggiungendone una, quindi memorizzandola di nuovo. Supponiamo di inizializzare questa variabile su 1, quindi creare due istanze di questo thread. Dopo che entrambi i thread sono terminati, l'intuizione suggerisce che questa variabile abbia un valore di 3. Tuttavia, la tabella seguente illustra cosa potrebbe andare storto:

Discussione 1 Thread 2
Ora Passo 1 Leggi 1 dalla variabile
Ora Passo 2 Leggi 1 dalla variabile
Ora Passo 3 Aggiungi 1 più 1 per ottenere 2
Fase 4 Aggiungi 1 più 1 per ottenere 2
Ora Passo 5 Memorizza 2 in variabile
Ora Passo 6 Memorizza 2 in variabile

Come puoi vedere, al termine dell'operazione, 2 è nella variabile, invece di 3. Il motivo è che il Thread 2 leggeva la variabile prima che il thread 1 venisse fatto aggiornandolo. La soluzione? Mutex.

Un mutex (portmanteau di mutual ex clusione) è un oggetto di gestione delle risorse progettato per risolvere questo tipo di problema. Quando un thread vuole accedere a una risorsa, "acquisisce" il mutex della risorsa. Una volta fatto l'accesso alla risorsa, il thread "rilascia" il mutex. Mentre il mutex viene acquisito, tutte le chiamate per acquisire il mutex non ritorneranno fino a quando il mutex non verrà rilasciato. Per capire meglio, pensate a un mutex come linea di attesa al supermercato: i fili si allineano cercando di acquisire il mutex e poi aspettando che i thread che li precedono finiscano, quindi usando la risorsa, quindi uscendo da linea rilasciando il mutex. Ci sarebbe un pandemonio completo se tutti provassero ad accedere alla risorsa contemporaneamente.

C ++ 11

std::mutex è l'implementazione di un mutex di 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow