C++
Beheer van middelen
Zoeken…
Invoering
Resource Acquisition is initialisatie
Resource Acquisition Is Initialization (RAII) is een veelgebruikt idioom in resource management. In het geval van dynamisch geheugen, gebruikt het slimme aanwijzers om resourcebeheer te bewerkstelligen. Wanneer u RAII gebruikt, wordt een verworven bron onmiddellijk eigendom van een slimme aanwijzer of een vergelijkbare bronbeheerder. De resource is alleen toegankelijk via deze manager, zodat de manager verschillende bewerkingen kan bijhouden. Bijvoorbeeld, std::auto_ptr
bevrijdt automatisch de bijbehorende bron wanneer deze buiten bereik valt of anderszins wordt verwijderd.
#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
's grootste probleem is dat het niet gekopieerd kan worden zonder eigendom over te dragen:
#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
}
Vanwege deze rare kopie-semantiek kan std::auto_ptr
onder andere niet in containers worden gebruikt. De reden hiervoor is om te voorkomen dat het geheugen twee keer wordt verwijderd: als er twee auto_ptrs
die eigenaar zijn van dezelfde bron, proberen ze beide deze te bevrijden wanneer ze worden vernietigd. Het vrijmaken van een al vrijgegeven bron kan over het algemeen problemen veroorzaken, dus het is belangrijk om het te voorkomen. std::shared_ptr
heeft echter een methode om dit te voorkomen, terwijl het eigendom niet wordt overgedragen tijdens het kopiëren:
#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
Mutexen en draadveiligheid
Er kunnen problemen optreden wanneer meerdere threads toegang proberen te krijgen tot een bron. Stel voor een eenvoudig voorbeeld dat we een thread hebben die er een aan een variabele toevoegt. Het doet dit door eerst de variabele te lezen, er één aan toe te voegen en vervolgens weer op te slaan. Stel dat we deze variabele initialiseren op 1 en vervolgens twee exemplaren van deze thread maken. Nadat beide threads zijn voltooid, suggereert intuïtie dat deze variabele een waarde van 3 moet hebben. De onderstaande tabel illustreert echter wat er mis kan gaan:
Discussie 1 | Discussie 2 | |
---|---|---|
Tijd Stap 1 | Lees 1 van variabele | |
Tijd Stap 2 | Lees 1 van variabele | |
Tijd Stap 3 | Voeg 1 plus 1 toe om 2 te krijgen | |
Tijd Stap 4 | Voeg 1 plus 1 toe om 2 te krijgen | |
Tijd Stap 5 | Sla 2 op in variabele | |
Tijd Stap 6 | Sla 2 op in variabele |
Zoals u kunt zien, zit aan het einde van de bewerking 2 in de variabele in plaats van 3. De reden is dat Thread 2 de variabele heeft gelezen voordat Thread 1 klaar was met bijwerken. De oplossing? Mutexes.
Een mutex (portmanteau van wederzijdse ex- clusie) is een object voor resourcebeheer dat is ontworpen om dit soort problemen op te lossen. Wanneer een thread toegang wil hebben tot een bron, "verwerft" deze de mutex van de bron. Zodra het klaar is met toegang tot de bron, "laat" de thread de mutex los. Terwijl de mutex wordt verkregen, zullen alle oproepen om de mutex te verkrijgen niet terugkeren totdat de mutex is vrijgegeven. Om dit beter te begrijpen, denk aan een mutex als een wachtlijn in de supermarkt: de threads gaan in lijn door te proberen de mutex te verkrijgen en dan te wachten tot de threads voor hen klaar zijn, gebruik vervolgens de resource en stap dan uit regel door de mutex vrij te geven. Er zou een complete pandemonium zijn als iedereen onmiddellijk toegang zou krijgen tot de bron.
std::mutex
is de implementatie van een mutex door 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
}