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

时间:2012-09-17 08:07:11

标签: c++ multithreading thread-safety mutex delete-operator

免责声明: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 );之后开始操作实例并删除对象之前?

3 个答案:

答案 0 :(得分:10)

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

  

我有一个程序,在其中我创建了一个Foo实例并且我运行   在许多线程中使用它。然后我想安全删除它   这些线程不会陷入分段错误。

您无法删除正在使用的对象。没有多少互斥量可以解决这个问题。

  

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

仅在删除Foo时运行。但是它的内容并不重要:当其他线程仍在使用Foo时调用dtor是错误的。

  

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

嗯,这是正确的问题。我可以编写大量代码供您使用,但该代码只是boost::weak_ptr的副本。所以,我不会打扰。只需自己动手代码即可。

  

不允许提升。

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:-1)

你发布的代码不正确,因为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 );
        }
    }
}