防止并发使用和释放

时间:2018-05-30 08:11:02

标签: c++ concurrency

考虑到在C ++内部实现了以下C-API

struct OpaqueObject;

struct OpaqueObject *allocateObject();
int deallocateObject(struct OpaqueObject *obj);

int useObject(struct OpaqueObject *obj);

同时分配,使用和取消分配几个不同的struct OpaqueObject - 实例是安全的。当然,不允许同时使用一个struct OpaqueObject - 实例,并且会产生未定义的行为。作为安全措施,struct OpaqueObject包含一个互斥锁,禁止这种情况:函数useObject()返回错误代码,如果多个线程尝试使用相同的struct OpaqueObject - 实例调用它。

struct OpaqueObject {
    std::mutex access;
    // ...
};

int useObject(struct OpaqueObject *obj) {
    if (!obj->access.try_lock()) {
        // different thread currently uses this obj
        return CONCURRENT_USE_ERROR;
    } else {
        // start using this obj
        // ...
        obj->access.unlock();
        return OK;
    }
}

但是,这种保障机制如何扩展到功能deallocateObject()?第一个天真的方法是

int deallocateObject(struct OpaqueObject *obj) {
    if (!obj->access.try_lock()) {
        // different thread currently uses this obj
        return CONCURRENT_USE_ERROR;
    } else {
        delete obj; // <--- (1)
        return OK;
    }
}

但是当它仍然被锁定时,销毁互斥锁​​是不明确的行为。我们无法在第(1)行之前解锁它,因为这会完全阻止我们阻止并发使用和释放的努力。

是否可以在useObject()deallocateObject()中返回错误,如果这些函数与同一struct OpaqueObject - 实例同时使用?

1 个答案:

答案 0 :(得分:1)

您可以将std::mutexstd::atomic<int>交换:

struct OpaqueObject {
    std::atomic<int> access = 0;
    // ...
};

然后在您的函数中,您可以自动交换值并查看它是否正在使用中:

int useObject(struct OpaqueObject *obj) {
    if (obj->access.exchange(1)) {
        // different thread currently uses this obj
        return CONCURRENT_USE_ERROR;
    } else {
        // start using this obj
        // ...
        obj->access.exchange(0);
        return OK;
    }
}

如果对象在使用变量access = 1且std::atomic::exchange将返回1.否则返回0并将access设置为1.

同样删除该对象也可以。

int deallocateObject(struct OpaqueObject *obj) {
    if (obj->access.exchange(1)) { // (*)
        // different thread currently uses this obj
        return CONCURRENT_USE_ERROR;
    } else {
        delete obj;                 // (**)
        return OK;
    }
}

重要提示:您是否考虑过删除对象后发生的事情?你如何通知其他线程删除它?