2

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

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 我可以从没有锁定它的线程中解锁互斥锁,对吗?第二种选择是否会受到任何虚假唤醒的影响?

4

1 回答 1

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对象实例的线程中创建销毁等待对象,否则可能会发生递归锁。还有更多,如果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 mutexshared_mutex当有很多FooSyncs等待时,似乎可以减轻锁的负担。

于 2012-06-28T12:08:36.717 回答