有一个由多个线程共享的对象来读取和写入,我需要使用具有以下功能的读写器锁来实现该类:
- 它可能被声明为由一个且不超过一个线程占用。任何其他试图占用它的线程都将被拒绝,并继续做他们的工作而不是被阻塞。
- 任何线程都可以随时询问对象是被自己占用还是被其他人占用,但被声明为占用或释放的时间除外。
- 只有对象的所有者才被允许释放其所有权,尽管其他人也可能会尝试这样做。如果不是所有者,释放操作将被取消。
- 性能需要仔细考虑。
我正在使用 OpenMP 进行这项工作,因此我希望仅使用 OpenMP 中的 API 而不是 POSIX 等来实现锁定。我已阅读此答案,但只有 C++ 标准库的实现解决方案。由于将 OpenMP 与 C++ 标准库或 POSIX 线程模型混合可能会减慢程序的速度,我想知道 OpenMP 有没有好的解决方案?
我试过这样,有时它工作得很好,但有时它崩溃了,有时它被死锁了。我发现也很难调试。
class Element
{
public:
typedef int8_t label_t;
Element() : occupied_(-1) {}
// Set it occupied by thread @myThread.
// Return whether it is set successfully.
bool setOccupiedBy(const int myThread)
{
if (lock_.try_lock())
{
if (occupied_ == -1)
{
occupied_ = myThread;
ready_.set(true);
}
}
// assert(lock_.get() && ready_.get());
return occupied_ == myThread;
}
// Return whether it is occupied by other threads
// except for thread @myThread.
bool isOccupiedByOthers(const int myThread) const
{
bool value = true;
while (lock_.get() != ready_.get());
value = occupied_ != -1 && occupied_ != myThread;
return value;
}
// Return whether it is occupied by thread @myThread.
bool isOccupiedBySelf(const int myThread) const
{
bool value = true;
while (lock_.get() != ready_.get());
value = occupied_ == myThread;
return value;
}
// Clear its occupying mark by thread @myThread.
void clearOccupied(const int myThread)
{
while (true)
{
bool ready = ready_.get();
bool lock = lock_.get();
if (!ready && !lock)
return;
if (ready && lock)
break;
}
label_t occupied = occupied_;
if (occupied == myThread)
{
ready_.set(false);
occupied_ = -1;
lock_.unlock();
}
// assert(ready_.get() == lock_.get());
}
protected:
Atomic<label_t> occupied_;
// Locked means it is occupied by one of the threads,
// and one of the threads might be modifying the ownership
MutexLock lock_;
// Ready means it is occupied by one the the threads,
// and none of the threads is modifying the ownership.
Mutex ready_;
};
原子变量、互斥锁和互斥锁使用 OpenMP 指令实现,如下所示:
template <typename T>
class Atomic
{
public:
Atomic() {}
Atomic(T&& value) : mutex_(value) {}
T set(const T& value)
{
T oldValue;
#pragma omp atomic capture
{
oldValue = mutex_;
mutex_ = value;
}
return oldValue;
}
T get() const
{
T value;
#pragma omp read
value = mutex_;
return value;
}
operator T() const { return get(); }
Atomic& operator=(const T& value)
{
set(value);
return *this;
}
bool operator==(const T& value) { return get() == value; }
bool operator!=(const T& value) { return get() != value; }
protected:
volatile T mutex_;
};
class Mutex : public Atomic<bool>
{
public:
Mutex() : Atomic<bool>(false) {}
};
class MutexLock : private Mutex
{
public:
void lock()
{
bool oldMutex = false;
while (oldMutex = set(true), oldMutex == true) {}
}
void unlock() { set(false); }
bool try_lock()
{
bool oldMutex = set(true);
return oldMutex == false;
}
using Mutex::operator bool;
using Mutex::get;
};
我还使用 OpenMP 提供的锁作为替代:
class OmpLock
{
public:
OmpLock() { omp_init_lock(&lock_); }
~OmpLock() { omp_destroy_lock(&lock_); }
void lock() { omp_set_lock(&lock_); }
void unlock() { omp_unset_lock(&lock_); }
int try_lock() { return omp_test_lock(&lock_); }
private:
omp_lock_t lock_;
};
顺便说一句,我在 x86_64 GNU/Linux 上使用 gcc 4.9.4 和 OpenMP 4.0。