C++
Resourcenmanagement
Suche…
Einführung
Ressourcenakquisition ist Initialisierung
Die Ressourcenerfassung ist die Initialisierung (RAII) ist ein allgemeiner Ausdruck in der Ressourcenverwaltung. Im Fall von dynamischem Speicher verwendet es intelligente Zeiger , um das Ressourcenmanagement durchzuführen. Bei Verwendung von RAII wird eine erworbene Ressource sofort einem intelligenten Zeiger oder einem gleichwertigen Ressourcenmanager zugeordnet. Auf die Ressource kann nur über diesen Manager zugegriffen werden, sodass der Manager verschiedene Vorgänge verfolgen kann. Beispiel: std::auto_ptr
die entsprechende Ressource automatisch frei, wenn sie außerhalb des Gültigkeitsbereichs liegt oder anderweitig gelöscht wird.
#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
}
Das Hauptproblem von std::auto_ptr
ist, dass es nicht kopiert werden kann, ohne den Besitzer zu übertragen:
#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
}
Aufgrund dieser seltsamen Kopiersemantik kann std::auto_ptr
unter anderem nicht in Containern verwendet werden. Der Grund ist, dass das Löschen des Speichers zweimal verhindert wird: Wenn zwei auto_ptrs
mit der gleichen Ressource vorhanden sind, versuchen beide, sie zu löschen, wenn sie zerstört werden. Das Freigeben einer bereits freigegebenen Ressource kann im Allgemeinen zu Problemen führen. Daher ist es wichtig, dies zu verhindern. std::shared_ptr
hat jedoch eine Methode, um dies zu vermeiden, während beim Kopieren der Besitz nicht übertragen wird:
#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
Mutexe & Fadensicherheit
Probleme können auftreten, wenn mehrere Threads versuchen, auf eine Ressource zuzugreifen. Angenommen, wir haben einen Thread, der einer Variablen einen hinzufügt. Dazu liest man zuerst die Variable, fügt eine hinzu und speichert sie dann zurück. Angenommen, wir initialisieren diese Variable auf 1 und erstellen dann zwei Instanzen dieses Threads. Nachdem beide Threads abgeschlossen sind, schlägt die Intuition vor, dass diese Variable den Wert 3 haben sollte. Die folgende Tabelle veranschaulicht jedoch, was möglicherweise schief geht:
Faden 1 | Faden 2 | |
---|---|---|
Zeitschritt 1 | Lese 1 aus Variable | |
Zeitschritt 2 | Lese 1 aus Variable | |
Zeitschritt 3 | Addiere 1 plus 1, um 2 zu erhalten | |
Zeitschritt 4 | Addiere 1 plus 1, um 2 zu erhalten | |
Zeitschritt 5 | Speichern Sie 2 in Variable | |
Zeitschritt 6 | Speichern Sie 2 in Variable |
Wie Sie sehen, befindet sich am Ende der Operation die Variable 2 anstelle von 3. Der Grund ist, dass der Thread 2 die Variable gelesen hat, bevor der Thread 1 seine Aktualisierung durchgeführt hat. Die Lösung? Mutexe
Ein Mutex ( Kunstwort aus mut ual ex schluss) ist ein Objekt Ressourcenmanagement entwickelt , um diese Art von Problem zu lösen. Wenn ein Thread auf eine Ressource zugreifen möchte, "erhält" er den Mutex der Ressource. Sobald der Zugriff auf die Ressource abgeschlossen ist, "gibt" der Thread den Mutex frei. Während der Mutex erworben wird, kehren alle Aufrufe zum Erwerb des Mutex nicht zurück, bis der Mutex freigegeben wird. Um dies besser zu verstehen, stellen Sie sich einen Mutex als Warteschlange im Supermarkt vor: Die Fäden gehen in die Reihe, indem sie versuchen, den Mutex zu erhalten, und warten, bis die vor ihnen liegenden Fäden fertiggestellt sind, dann die Ressource nutzen und dann aussteigen Linie durch Freigabe des Mutex. Wenn alle versuchen, sofort auf die Ressource zuzugreifen, gäbe es ein komplettes Pandemium.
std::mutex
ist die Implementierung eines Mutex in 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
}