如何让线程等待销毁对象

时间:2012-06-28 11:01:29

标签: c++ synchronization pthreads conditional-statements

我希望有一个线程等待另一个线程破坏特定对象。我想以某种方式实现它:

class Foo {
private:
    pthread_mutex_t* mutex;
    pthread_cond_t* condition;
public:
    Foo(pthread_mutex_t* _mutex, pthread_cond_t* _condition) : mutex(_mutex), condition(_condition) {}

    void waitForDestruction(void) {
        pthread_mutex_lock(mutex);
        pthread_cond_wait(condition,mutex);
        pthread_mutex_unlock(mutex);
    }

    ~Foo(void) {
        pthread_mutex_lock(mutex);
        pthread_cond_signal(condition);
        pthread_mutex_unlock(mutex);
    }
};

然而,我知道我必须处理waitForDestruction方法中的虚假唤醒,但是我不能在'this'上调用任何东西,因为它已经被破坏了。

我想到的另一种可能性是不使用条件变量,而是在构造函数中锁定互斥锁,在析构函数中解锁它并在waitForDestruction方法中锁定/解锁它 - 这应该适用于非递归互斥锁,和iirc我可以从没有锁定它的线程中解锁互斥锁,对吧?第二种选择会遭受任何虚假的唤醒吗?

1 个答案:

答案 0 :(得分:1)

这总是一件困难的事情。但是这些代码行如何:

struct FooSync {

    typedef boost::shared_ptr<FooSync> Ptr;

    FooSync() : owner(boost::this_thread::get_id()) {
    }

    void Wait() {
        assert(boost::this_thread::get_id() != owner);
        mutex.lock();
        mutex.unlock();
    }

    boost::mutex mutex;
    boost::thread::id owner;

};

struct Foo {

    Foo() { }

    ~Foo() {
        for (size_t i = 0; i < waiters.size(); ++i) {
            waiters[i]->mutex.unlock();
        }
    }

    FooSync::Ptr GetSync() {
        waiters.push_back(FooSync::Ptr(new FooSync));
        waiters.back()->mutex.lock();
        return waiters.back();
    }

    std::vector<FooSync::Ptr> waiters;

};

上面的解决方案将允许单个Foo对象上的任意数量的销毁等待对象。只要它能正确管理这些对象占用的内存。似乎没有什么能阻止在堆栈上创建Foo个实例。

虽然我看到的唯一缺点是它需要始终在“拥有”Foo对象实例的线程中创建destroy-wait对象,否则可能会发生递归锁定。还有更多,如果GetSync从多个线程调用,则push_back之后可能会出现竞争条件。

<小时/>

编辑:

好的,我已经重新考虑了这个问题并提出了新解决方案。看看:

typedef boost::shared_ptr<boost::shared_mutex> MutexPtr;

struct FooSync {

    typedef boost::shared_ptr<FooSync> Ptr;

    FooSync(MutexPtr const& ptr) : mutex(ptr) {
    }

    void Wait() {
        mutex->lock_shared();
        mutex->unlock_shared();
    }

    MutexPtr mutex;

};

struct Foo {

    Foo() : mutex(new boost::shared_mutex) {
        mutex->lock();
    }

    ~Foo() {
        mutex->unlock();
    }

    FooSync::Ptr GetSync() {
        return FooSync::Ptr(new FooSync(mutex));
    }

    MutexPtr mutex;

};

现在它似乎相当清洁,而且代码点数远远少于竞争条件。在对象本身和所有同步对象之间只共享一个同步原语。必须采取一些措施来克服在对象本身所在的线程中调用Wait的情况(如我的第一个示例中所示)。如果目标平台不支持shared_mutex,则可以使用good-ol mutex。当shared_mutex等待很多时,FooSync似乎减轻了锁的负担。