从多线程程序中安全地删除对象

Delete an object securely from a multi-threaded program

本文关键字:删除 对象 安全 多线程 程序      更新时间:2023-10-16

免责声明:既不允许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 );和删除对象之前锁定互斥并开始操作实例?

让我们从问题的开头开始:

我有一个程序,在其中我创建了一个Foo实例并进行操作在多个线程中使用它。然后我想安全地删除它,这样这些线程不会陷入分段故障。

您不能删除正在使用的对象。再多的互斥也无法解决这个问题。

我在Foo 中添加了一个析构函数

只有在删除Foo时才会运行此操作。不过,它的内容并不重要:当其他线程仍在使用Foo时调用dtor是错误的。

我希望线程在删除实例时安全退出。这怎么可能?

好吧,这是正确的问题。我可以写很多代码供您使用,但这些代码只是boost::weak_ptr的副本。所以,我就不麻烦了。你自己拿助推码就行了。

不允许Boost。

那你为什么要问StackOverflow?这基本上是相同的许可证。

这里缺少的是指示线程处理何时完成的条件。删除特定对象实例不是一个好条件。您还没有向我们显示删除对象的位置。如果我们能在代码中看到这一点,那么额外的上下文将很有帮助。

我建议不要删除对象,而是在对象上设置一个标志(例如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()完成之后。

您发布的代码是不对的,因为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 );
}
}
}