C++
Resurshantering
Sök…
Introduktion
Resursförvärv är initialisering
Resource Acquisition Is Initialization (RAII) är ett vanligt formspråk i resurshantering. När det gäller dynamiskt minne använder den smarta pekare för att åstadkomma resurshantering. När du använder RAII ges en förvärvad resurs omedelbart äganderätt till en smart pekare eller motsvarande resurshanterare. Resursen nås endast via den här hanteraren, så chefen kan hålla reda på olika operationer. Till exempel frigör std::auto_ptr
automatiskt motsvarande resurs när den faller utanför räckvidden eller på annat sätt raderas.
#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
huvudproblem är att det inte kan kopieras utan att överföra ägande:
#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
}
På grund av dessa konstiga std::auto_ptr
kan std::auto_ptr
inte användas i containrar, bland annat. Anledningen till att det gör detta är att förhindra att minnet auto_ptrs
bort två gånger: om det finns två auto_ptrs
äger samma resurs, försöker de båda frigöra det när de förstörs. Att frigöra en redan befriad resurs kan generellt orsaka problem, så det är viktigt att förhindra det. std::shared_ptr
har emellertid en metod för att undvika detta utan att överföra äganderätt vid kopiering:
#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 & gängesäkerhet
Problem kan inträffa när flera trådar försöker komma åt en resurs. För ett enkelt exempel, anta att vi har en tråd som lägger till en variabel. Det gör detta genom att först läsa variabeln, lägga till en i den och sedan lagra den tillbaka. Anta att vi initialiserar denna variabel till 1 och skapar sedan två instanser av den här tråden. Efter att båda trådarna har avslutats antyder intuitionen att denna variabel ska ha ett värde på 3. Tabellen nedan illustrerar dock vad som kan gå fel:
Tråd 1 | Tråd 2 | |
---|---|---|
Tid Steg 1 | Läs 1 från variabel | |
Tid Steg 2 | Läs 1 från variabel | |
Tid Steg 3 | Lägg till 1 plus 1 för att få 2 | |
Tid Steg 4 | Lägg till 1 plus 1 för att få 2 | |
Tid Steg 5 | Förvara 2 i variabel | |
Tid Steg 6 | Förvara 2 i variabel |
Som ni ser är 2 i operationens slut 2 i variabeln istället för 3. Anledningen är att tråd 2 läste variabeln innan tråd 1 gjordes för att uppdatera den. Lösningen? Mutexes.
En mutex (kappsäck av mut UAL ex slutsats) är en resurs förvaltningsobjekt för att lösa den här typen av problem. När en tråd vill komma åt en resurs "förvärvar" resursens mutex. När det är gjort med åtkomst till resursen "släpper" tråden mutex. Medan mutex förvärvas kommer alla samtal för att förvärva mutex inte tillbaka förrän mutex släpps. För att bättre förstå detta, tänk på en mutex som en väntelinje i snabbköpet: trådarna går i linje genom att försöka skaffa mutexen och sedan vänta på att trådarna framför dem ska slutföras, sedan använda resursen och sedan kliva ut ur rad genom att släppa mutex. Det skulle vara komplett pandemonium om alla försökte komma åt resursen på en gång.
std::mutex
är C ++ 11: s implementering av en mutex.
#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
}