自旋锁定堆栈和内存屏障 (C++)
Spin locked stack and memory barriers (C++)
我有一个实现自旋锁:
class Spinlock {
public:
void Lock() {
while (true) {
if (!_lock.test_and_set(std::memory_order_acquire)) {
return;
}
}
}
void Unlock() {
_lock.clear(std::memory_order_release);
}
private:
std::atomic_flag _lock;
};
我在以下地方使用 SpinLock class:
class SpinlockedStack {
public:
SpinlockedStack() : _head(nullptr) {
}
~SpinlockedStack() {
while (_head != nullptr) {
Node* node = _head->Next;
delete _head;
_head = node;
}
}
void Push(int value) {
_lock.Lock();
_head = new Node(value, _head);
_lock.Unlock();
}
bool TryPop(int& value) {
_lock.Lock();
if (_head == nullptr) {
value = NULL;
_lock.Unlock();
return false;
}
Node* node = _head;
value = node->Value;
_head = node->Next;
delete node;
_lock.Unlock();
return true;
}
private:
struct Node {
int Value;
Node* Next;
Node(int value, Node* next) : Value(value), Next(next) {
}
};
Node* _head;
Spinlock _lock;
};
我知道我应该设置内存屏障。我可以使用原子变量:
struct Node {
int Value;
std::atomic<Node*> Next;
Node(int value) : Value(value) {
}
};
std::atomic<Node*> _head;
Spinlock _lock;
...
void Push(int value) {
_lock.Lock();
Node* currentHead = _head.load(std::memory_order_acquire);
Node* newHead = new Node(value);
newHead->Next.store(currentHead, std::memory_order_relaxed);
_head.store(newHead, std::memory_order_release);
_lock.Unlock();
}
bool TryPop(int& value) {
_lock.Lock();
Node* currentHead = _head.load(std::memory_order_acquire);
if (currentHead == nullptr) {
value = NULL;
_lock.Unlock();
return false;
}
value = currentHead->Value;
_head.store(currentHead->Next.load(std::memory_order_relaxed), std::memory_order_release);
delete currentHead;
_lock.Unlock();
return true;
}
我也可以使用 atomic_thread_fence():
struct Node {
int Value;
Node* Next;
Node(int value) : Value(value) {
}
};
Node* _head;
Spinlock _lock;
...
void Push(int value) {
_lock.Lock();
Node* currentHead = _head;
std::atomic_thread_fence(std::memory_order_acquire);
Node* newHead = new Node(value);
newHead->Next = currentHead;
std::atomic_thread_fence(std::memory_order_release);
_head = newHead;
_lock.Unlock();
}
bool TryPop(int& value) {
_lock.Lock();
std::atomic_thread_fence(std::memory_order_acquire);
Node* currentHead = _head;
if (currentHead == nullptr) {
value = NULL;
_lock.Unlock();
return false;
}
value = currentHead->Value;
std::atomic_thread_fence(std::memory_order_acquire);
Node* nextNead = currentHead->Next;
std::atomic_thread_fence(std::memory_order_release);
_head = nextNead;
delete currentHead;
_lock.Unlock();
return true;
}
我的问题:
- 我是否设置了内存屏障?
- 在这种情况下使用什么更好(原子变量或 atomic_thread_fence),为什么?
锁的获取已经建立了你需要的内存保证。
当线程释放锁时,它必须写入原子标志。这保证了当下一个线程获取锁并看到对标志的写入时,获取线程保证看到释放线程在写入标志之前所做的所有写入。
在旁注中,您应该使用 RAII 之类的东西来确保您的锁在所有情况下都已释放。
此外,您还必须使用 ATOMIC_FLAG_INIT 初始化您的锁,否则它处于未定义状态。
我有一个实现自旋锁:
class Spinlock {
public:
void Lock() {
while (true) {
if (!_lock.test_and_set(std::memory_order_acquire)) {
return;
}
}
}
void Unlock() {
_lock.clear(std::memory_order_release);
}
private:
std::atomic_flag _lock;
};
我在以下地方使用 SpinLock class:
class SpinlockedStack {
public:
SpinlockedStack() : _head(nullptr) {
}
~SpinlockedStack() {
while (_head != nullptr) {
Node* node = _head->Next;
delete _head;
_head = node;
}
}
void Push(int value) {
_lock.Lock();
_head = new Node(value, _head);
_lock.Unlock();
}
bool TryPop(int& value) {
_lock.Lock();
if (_head == nullptr) {
value = NULL;
_lock.Unlock();
return false;
}
Node* node = _head;
value = node->Value;
_head = node->Next;
delete node;
_lock.Unlock();
return true;
}
private:
struct Node {
int Value;
Node* Next;
Node(int value, Node* next) : Value(value), Next(next) {
}
};
Node* _head;
Spinlock _lock;
};
我知道我应该设置内存屏障。我可以使用原子变量:
struct Node {
int Value;
std::atomic<Node*> Next;
Node(int value) : Value(value) {
}
};
std::atomic<Node*> _head;
Spinlock _lock;
...
void Push(int value) {
_lock.Lock();
Node* currentHead = _head.load(std::memory_order_acquire);
Node* newHead = new Node(value);
newHead->Next.store(currentHead, std::memory_order_relaxed);
_head.store(newHead, std::memory_order_release);
_lock.Unlock();
}
bool TryPop(int& value) {
_lock.Lock();
Node* currentHead = _head.load(std::memory_order_acquire);
if (currentHead == nullptr) {
value = NULL;
_lock.Unlock();
return false;
}
value = currentHead->Value;
_head.store(currentHead->Next.load(std::memory_order_relaxed), std::memory_order_release);
delete currentHead;
_lock.Unlock();
return true;
}
我也可以使用 atomic_thread_fence():
struct Node {
int Value;
Node* Next;
Node(int value) : Value(value) {
}
};
Node* _head;
Spinlock _lock;
...
void Push(int value) {
_lock.Lock();
Node* currentHead = _head;
std::atomic_thread_fence(std::memory_order_acquire);
Node* newHead = new Node(value);
newHead->Next = currentHead;
std::atomic_thread_fence(std::memory_order_release);
_head = newHead;
_lock.Unlock();
}
bool TryPop(int& value) {
_lock.Lock();
std::atomic_thread_fence(std::memory_order_acquire);
Node* currentHead = _head;
if (currentHead == nullptr) {
value = NULL;
_lock.Unlock();
return false;
}
value = currentHead->Value;
std::atomic_thread_fence(std::memory_order_acquire);
Node* nextNead = currentHead->Next;
std::atomic_thread_fence(std::memory_order_release);
_head = nextNead;
delete currentHead;
_lock.Unlock();
return true;
}
我的问题:
- 我是否设置了内存屏障?
- 在这种情况下使用什么更好(原子变量或 atomic_thread_fence),为什么?
锁的获取已经建立了你需要的内存保证。
当线程释放锁时,它必须写入原子标志。这保证了当下一个线程获取锁并看到对标志的写入时,获取线程保证看到释放线程在写入标志之前所做的所有写入。
在旁注中,您应该使用 RAII 之类的东西来确保您的锁在所有情况下都已释放。
此外,您还必须使用 ATOMIC_FLAG_INIT 初始化您的锁,否则它处于未定义状态。