简体   繁体   English

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

[英]Spin locked stack and memory barriers (C++)

I have a implementation spin lock: 我有一个实现自旋锁:

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

I use SpinLock class in: 我在下面使用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;
};

I understand that i should put memory barriers. 我明白我应该设置记忆障碍。 I can use atomic variables: 我可以使用原子变量:

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

I can also use atomic_thread_fence(): 我也可以使用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;
}

My questions: 我的问题:

  1. Do I place the memory barriers? 我是否设置了内存屏障?
  2. What is better to use in this case (atomic variables or atomic_thread_fence) and why? 在这种情况下最好使用什么(原子变量或atomic_thread_fence)以及为什么?

The acquiring of the lock already establishes the memory guarantees that you need. 获取锁已经确定了您需要的内存保证。

When a thread releases the lock it has to write to the atomic flag. 当一个线程释放锁时,它必须写入原子标志。 This guarantees that when the next thread acquires the lock and sees the write to the flag, that the acquiring thread is guaranteed to see all writes the releasing thread did before writing to the flag. 这保证了当下一个线程获得锁并看到对该标志的写入时,获取线程保证在写入该标志之前看到释放线程所做的所有写操作。

On a sidenote you should use something like RAII to make sure your lock is released in all circumstances. 在旁注中,您应该使用RAII之类的东西来确保在所有情况下释放锁定。

Also you have to initialize your lock with ATOMIC_FLAG_INIT otherwise it is on an undefined state. 此外,您必须使用ATOMIC_FLAG_INIT初始化锁定,否则它将处于未定义状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM