Как безопасно уничтожить объект, к которому часто обращаются два разных потока?

В настоящее время я столкнулся с проблемой, что объект (экземпляр), к которому часто обращаются два разных потока, должен быть освобожден. Для меня не имеет значения, какой из двух потоков разрушает экземпляр, но я бы предпочел тот, который также создает его, хотя я думаю, что это не имеет значения вообще.

Таким образом, в сценарии, где поток, который должен уничтожить объект, обнаруживает, что он должен быть удален, и при вызове деструктора другой поток обращается к члену (функции) этого объекта, возможно, произойдет какая-то ошибка времени выполнения. ,

Я провел некоторое исследование по этой теме, но я мог просто найти людей, говорящих: «Зачем удалять Объект, который все еще необходим для существования». Но в моем случае это должно перестать быть необходимым после того, как кусок кода, который выполняет один поток, решит уничтожить его.

Я был бы признателен за ответ, такой как подсказка для хорошей книги или статьи, которая охватывает эту тему, но не стесняйтесь писать, как бы вы решили эту проблему.

1

Решение

Для одновременного доступа к данным вам потребуется двойное косвенное обращение:

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.
  • shared_ptr atomic_load (const shared_ptr * p);
  • void atomic_store (shared_ptr * p, shared_ptr r);

  • Но я не знаком с этими функциями.
1

Другие решения

Нить, которая должна не уничтожить объект должен держать его с помощью std::weak_ptrЗа исключением случаев, когда он активно его использует. В эти времена weak_ptr может быть повышен до std::shared_ptr,

Нить, которая должна уничтожить его, теперь может удерживать его shared_ptr до тех пор, пока он считает целесообразным, и отказаться от этого shared_ptr после этого. Если другой поток имеет только weak_ptr (не активно его использую) объект исчезнет. если другой поток также имеет shared_ptr, он удерживает объект до завершения операции.

0

По вопросам рекламы [email protected]