C++
Gestione delle risorse
Ricerca…
introduzione
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
}
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.
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
}