Безопасное удаление объекта из многопоточной программы

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: ни Boost, ни C ++ 11 не допускаются.

У меня есть программа, в которой я создаю экземпляр Foo и я оперирую им в нескольких потоках. Затем я хочу удалить его безопасно, чтобы эти потоки не попадали в ошибку сегментации.

Я добавил члена мьютекса в Foo и блокировать его каждый раз, когда запускается функция потока. Для того чтобы разные темы не конфликтовали друг с другом.

class Foo
{
public:
pthread_mutex_t mutex;
};

void* thread ( void* fooPtr )
{
Foo* fooPointer = (Foo*) fooPtr;

while ( 1 )
{
if ( fooPointer )
{
pthread_mutex_lock ( &fooPointer->mutex );
/* some code, involving fooPointer */
pthread_mutex_unlock ( &fooPointer->mutex );
}
else
{
pthread_exit ( NULL );
}
}
}

Теперь я хочу удалить foo надежно, чтобы в потоках не возникало ошибок. Я добавил деструктор Foo:

Foo::~Foo()
{
pthread_mutex_lock ( &mutex );
}

Теперь объект не будет удален, пока все потоки не завершат текущий цикл.

Вопрос: будет ли разблокирован мьютекс после удаления экземпляра? Завершатся ли все потоки после удаления экземпляра? Бьюсь об заклад, ответ no, Поэтому я изменяю деструктор, но теперь он кажется небезопасным:

Foo::~Foo()
{
pthread_mutex_lock ( &mutex );
pthread_mutex_unlock ( &mutex );
}

Может ли функция потока заблокировать мьютекс и начать работу с экземпляром после pthread_mutex_unlock ( &mutex ); и ДО объекта удаляется?

0

Решение

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

Я бы предложил вместо удаления объекта установить флаг на объект (например, bool active ()). Этот флаг будет проверен всеми потоками, и когда он укажет на остановку обработки, потоки остановятся. Установите этот флаг там, где вы сейчас удаляете объект Foo. Затем, как только все потоки остановились, удалите объект Foo.

Если вы удалите объект и ожидаете, что сможете получить его блокировку мьютекса, вы, возможно, получите аварийное завершение или, по крайней мере, нестабильное поведение, поскольку мьютекс является членом Foo, и он будет уничтожен вместе с объектом.

Вот пример того, что я имею в виду:

class Foo
{
public:
void lockMutex();
void unlockMutex();
// Active should be mutex protected as well
// Or you could consider using a pthread_rwlock
bool active() {return active_;}
void setActive(bool a) {active_ = a;}
private:
pthread_mutex_t mutex;
bool active_;
};

void* thread ( void* fooPtr )
{
Foo* fooPointer = (Foo*) fooPtr;

while ( 1 )
{
if ( fooPointer->active() )
{
fooPointer->lockMutex();
/* some code, involving fooPointer */
fooPointer->unlockMutex();
}
else
{
pthread_exit ( NULL );
}
}

// somewhere else in the code
fooPointer->setActive(false);
}

Foo :: setActive (true) должен вызываться либо в конструкторе, либо при создании объекта. И объект Foo должен быть удален после остановки потоков, скорее всего после завершения pthread_join ().

1

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

Давайте начнем с начала вашего вопроса:

У меня есть программа, в которой я создаю экземпляр Foo, и я работаю
с этим в ряде потоков. Тогда я хочу удалить это безопасно, так
эти потоки не попадают в ошибку сегментации.

Вы не можете удалить объект, который используется. Никакое количество мьютексов не исправит это.

Я добавил деструктор в Foo

Это работает только тогда, когда Foo удаляется. Однако его содержание не имеет большого значения: неправильно вызывать dtor, пока другие потоки все еще используют Foo.

Я хочу, чтобы поток безопасно завершал работу при удалении экземпляра. Как это возможно?

Ну, это правильный вопрос. Я мог бы написать целый код для вас, но этот код будет просто копией boost::weak_ptr, Так что я не буду беспокоиться. Просто возьмите буст-код самостоятельно.

Повышение не допускается.

Тогда почему вы спрашиваете о StackOverflow? Это по сути та же лицензия.

10

Ваш опубликованный код неверен, потому что объект c ++ уничтожается пошагово:

obj->Foo::~Foo();

free memory //maybe delete if allocated by new

Таким образом, ваш источник только защищает деструктор, но не освобождает память.

может быть, вам поможет исходный код, он простой и грубый, но я думаю, что он может работать

    class Foo
{

public:
void dosomething() {}
};

template<typename T>
class Protect
{
public:
struct auto_lock {
auto_lock(pthread_mutex_t& mutex)
: _mutex(mutex)
{
pthread_mutex_lock ( &_mutex );
}
~ auto_lock()
{
pthread_mutex_unlock ( &_mutex );
}
pthread_mutex_t& _mutex;
};

Protect(T*& p): _p(p) {}
T* get() { return _p; }

void lock() { pthread_mutex_lock ( &_mutex ); }
void unlock() { pthread_mutex_unlock ( &_mutex );}
pthread_mutex_t& getlock() { return _mutex; }

void safe_release() {
auto_lock l(_mutex);
if (_p != NULL)  {
delete _p;
_p = NULL;
}
}
private:
T*& _p;
pthread_mutex_t _mutex;
};

void* thread ( void* proPtr )
{
Protect<Foo>* proPointer = (Protect<Foo>*) proPtr;

while ( 1 )
{
Protect<Foo>::auto_lock l(proPointer->getlock());
Foo* fooPtr = proPointer->get();
if ( fooPtr )
{
fooPtr->dosomething();
}
else
{
pthread_exit ( NULL );
}
}
}
-1
По вопросам рекламы [email protected]