C++
Управление ресурсами
Поиск…
Вступление
Инициализация ресурсов
Приобретение ресурсов - это инициализация (RAII) - распространенная идиома в управлении ресурсами. В случае динамической памяти он использует интеллектуальные указатели для выполнения управления ресурсами. При использовании RAII приобретенный ресурс сразу получает право владения интеллектуальному указателю или эквивалентному менеджеру ресурсов. Доступ к ресурсу осуществляется только через этого менеджера, поэтому менеджер может отслеживать различные операции. Например, std::auto_ptr
автоматически освобождает свой соответствующий ресурс, когда он выпадает из области действия или иным образом удаляется.
#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
заключается в том, что он не может копироваться без передачи права собственности:
#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
}
Из-за этой странной семантики копирования, std::auto_ptr
нельзя использовать в контейнерах, между прочим. Причина этого заключается в том, чтобы предотвратить удаление памяти дважды: если есть два auto_ptrs
с правом собственности на один и тот же ресурс, они обе пытаются освободить его, когда они будут уничтожены. Освобождение уже освобожденного ресурса может вызвать проблемы, поэтому важно предотвратить его. Тем не менее, std::shared_ptr
имеет способ избежать этого, не передавая право собственности при копировании:
#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
Мьютексы и безопасность потоков
Проблемы могут возникать, когда несколько потоков пытаются получить доступ к ресурсу. Для простого примера предположим, что у нас есть поток, который добавляет его к переменной. Он делает это, сначала прочитав переменную, добавив ее к ней, а затем сохранит ее. Предположим, что мы инициализируем эту переменную до 1, затем создаем два экземпляра этого потока. По завершении обоих потоков интуиция предполагает, что эта переменная должна иметь значение 3. Однако приведенная ниже таблица иллюстрирует, что может пойти не так:
Тема 1 | Тема 2 | |
---|---|---|
Время Шаг 1 | Чтение 1 из переменной | |
Время Шаг 2 | Чтение 1 из переменной | |
Время Шаг 3 | Добавьте 1 плюс 1, чтобы получить 2 | |
Время Шаг 4 | Добавьте 1 плюс 1, чтобы получить 2 | |
Время Шаг 5 | Хранить 2 в переменной | |
Время Шаг 6 | Хранить 2 в переменной |
Как вы можете видеть, в конце операции 2 находится в переменной, а не 3. Причина в том, что Thread 2 прочитал переменную до того, как был обновлен Thread 1. Решение? Мьютексы.
Взаимная блокировка (контаминация Мут UAL экс ключения) является объектом управления ресурсами предназначен для решения такого рода проблем. Когда поток хочет получить доступ к ресурсу, он «приобретает» мьютекс ресурса. Как только это делается, доступ к ресурсу, поток «освобождает» мьютекс. Пока мьютекс будет получен, все вызовы для получения мьютекса не вернутся, пока мьютекс не будет отпущен. Чтобы лучше понять это, подумайте о мьютексе как очереди ожидания в супермаркете: потоки идут в линию, пытаясь получить мьютексы, а затем ожидают, что потоки впереди них закончатся, а затем используя ресурс, а затем выйдут из путем освобождения мьютекса. Было бы полное пандемониум, если бы каждый попытался получить доступ к ресурсу сразу.
std::mutex
- это реализация std::mutex
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
}