返回

锁和基于锁的数据结构

后端

理解锁和数据结构的关系

在多线程程序中,共享资源访问需要确保一致性。此时,锁机制应运而生。通过使用不同的锁策略,可以实现对数据结构的保护,避免竞态条件的发生。

互斥锁(Mutex Lock)

最基本的锁是互斥锁,它用于控制多个线程间的顺序执行,保证同一时间只有一个线程能访问资源。

代码示例:

#include <mutex>
std::mutex mtx;
void critical_section() {
    mtx.lock();
    // 执行关键区段操作
    mtx.unlock();
}

读写锁(Read-Write Lock)

对于某些场景,例如大量读者和少量写者,互斥锁显得效率较低。这时可以采用读写锁来提高并发性能。

代码示例:

#include <shared_mutex>
std::shared_timed_mutex rw_mtx;

void reader_operation() {
    std::shared_lock<std::shared_timed_mutex> lock(rw_mtx);
    // 读取操作
}

void writer_operation() {
    std::unique_lock<std::shared_timed_mutex> lock(rw_mtx);
    // 写入操作
}

构建线程安全的数据结构

基于锁的保护,接下来构建几种常用且重要的数据结构。

线程安全栈(Thread-Safe Stack)

利用互斥锁实现线程安全的堆栈。

代码示例:

template <typename T>
class ThreadSafeStack {
private:
    std::stack<T> stack_;
    std::mutex mutex_;

public:
    void push(T const& data) {
        std::lock_guard<std::mutex> lock(mutex_);
        stack_.push(data);
    }

    T pop() {
        std::lock_guard<std::mutex> lock(mutex_);
        T result = stack_.top();
        stack_.pop();
        return result;
    }
};

线程安全队列(Thread-Safe Queue)

线程安全的队列同样重要,它支持多生产者和多消费者模式。

代码示例:

#include <condition_variable>
template<typename T>
class ThreadSafeQueue {
private:
    std::queue<T> queue_;
    mutable std::mutex mutex_;
    std::condition_variable condition_;

public:
    void push(T const& data) {
        std::lock_guard<std::mutex> lock(mutex_);
        queue_.push(data);
        condition_.notify_one();
    }

    T pop() {
        std::unique_lock<std::mutex> lock(mutex_);
        while (queue_.empty()) {
            condition_.wait(lock);
        }
        T result = queue_.front();
        queue_.pop();
        return result;
    }
};

安全建议

  • 在使用锁时,尽量减少锁的持有时间。
  • 尽可能地避免死锁,例如使用标准库中的std::lock_guardstd::unique_lock来自动管理锁生命周期。
  • 当多个资源需要同时被锁定时,遵循固定的顺序以防止死锁。

结论

通过上述例子可以发现,在构建线程安全的数据结构时,合理地利用不同的锁机制对于提高程序效率至关重要。选择合适的锁策略不仅能避免数据竞争,还能够提升程序的并发性能。


相关资源链接:

以上资源提供更深入的锁和并发编程理解,适合开发者进一步学习。