自旋锁定堆栈和内存屏障(C++)

Spin locked stack and memory barriers (C++)

本文关键字:C++ 内存 锁定 堆栈      更新时间:2023-10-16

我有一个实现旋转锁:

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 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;
}

我的问题:

  1. 我会设置记忆障碍吗
  2. 在这种情况下,使用什么更好(原子变量或atomic_thread_fence)?为什么

锁的获取已经建立了所需的内存保证。

当线程释放锁时,它必须写入原子标志。这保证了当下一个线程获取锁并看到对标志的写入时,获取线程保证看到释放线程在写入标志之前所做的所有写入。

顺便说一句,你应该使用类似RAII的东西来确保你的锁在任何情况下都被释放。

此外,您必须使用ATOMIC_FLAG_INIT初始化您的锁,否则它将处于未定义状态。