В настоящее время я столкнулся с проблемой, что объект (экземпляр), к которому часто обращаются два разных потока, должен быть освобожден. Для меня не имеет значения, какой из двух потоков разрушает экземпляр, но я бы предпочел тот, который также создает его, хотя я думаю, что это не имеет значения вообще.
Таким образом, в сценарии, где поток, который должен уничтожить объект, обнаруживает, что он должен быть удален, и при вызове деструктора другой поток обращается к члену (функции) этого объекта, возможно, произойдет какая-то ошибка времени выполнения. ,
Я провел некоторое исследование по этой теме, но я мог просто найти людей, говорящих: «Зачем удалять Объект, который все еще необходим для существования». Но в моем случае это должно перестать быть необходимым после того, как кусок кода, который выполняет один поток, решит уничтожить его.
Я был бы признателен за ответ, такой как подсказка для хорошей книги или статьи, которая охватывает эту тему, но не стесняйтесь писать, как бы вы решили эту проблему.
Для одновременного доступа к данным вам потребуется двойное косвенное обращение:
class Object {
public;
class Data {
int get_value() const;
void set_value(int);
};
class Guard {
public:
Guard(Nutex& mutex, Data* data)
: data(data)
{
if( ! data) throw std::runtime_error("No Data");
mutex.lock();
}
~Guard()
{
mutex.unlock();
}
Data& data() { return *m_data; }
private:
Data* data;
};
class ConstGuard {
public:
ConstGuard(Mutex& mutex, const Data* data)
: data(data)
{
if( ! data) throw std::runtime_error("No Data");
mutex.lock();
}
~ConstGuard()
{
mutex.unlock();
}
const Data& data() const { return *m_data; }
private:
const Data* data;
};
private:
friend std::shared_ptr<Object> make_object(const Data&);
Object(const Data& data)
: data(new Data(data))
{}
public:
~Object()
{
delete data;
}
/// Self destruction.
void dispose() {
Guard guard(get());
delete data;
data = 0;
}
// For multiple operations use a Guard.
Guard get() { return Guard(mutex, data); }
ConstGuard get() const { return ConstGuard(mutex, data); }
// For single operations you may provide convenience functions.
int get_value() const { return ConstGuard(mutex, data).data().get_value(); }
void set_value(int value) { Guard(mutex, data).data().set_value(value); }
private:
mutable Mutex mutex;
data* data;
};
std::shared_ptr<Object> make_object(const Object::Data& data) {
return std::make_shared<Object>(data);
}
(Примечание: приведенный выше код является просто наброском, я его не скомпилировал)
Это была длинная история. Коротким из них является [20.7.2.5] shared_ptr атомарный доступ:
Concurrent access to a shared_ptr object from multiple threads does not
introduce a data race if the access is done exclusively via the functions
in this section and the instance is passed as their first argument.
Нить, которая должна не уничтожить объект должен держать его с помощью std::weak_ptr
За исключением случаев, когда он активно его использует. В эти времена weak_ptr
может быть повышен до std::shared_ptr
,
Нить, которая должна уничтожить его, теперь может удерживать его shared_ptr
до тех пор, пока он считает целесообразным, и отказаться от этого shared_ptr
после этого. Если другой поток имеет только weak_ptr
(не активно его использую) объект исчезнет. если другой поток также имеет shared_ptr
, он удерживает объект до завершения операции.