lru python实现-扩展一 线程安全

77 阅读2分钟

首先,lru是一个cache,使用方式是:
lru cache(1024)
lru.get(key)
lru.put(key,value)

当我们说线程安全的时候,指有一个全局变量lru cache, 多个线程都能读写,要并发访问控制;

实现方式也比较简单,就是给lru这个类添加一个lock属性就可以了。

下面是一个使用 Python 实现的 线程安全的 LRU Cache(最近最少使用缓存) ,采用:

  • 自定义双向链表 + 字典(dict) :用于快速访问和维护访问顺序。
  • threading.Lock:保证在多线程环境下操作的安全性。

✅ 功能要求

  • 支持 get(key) 和 put(key, value) 操作。
  • 当容量满时,自动删除最久未使用的条目(LRU 策略)。
  • 支持并发访问(线程安全)。

🧱 核心结构说明

  1. 双向链表节点类(Node

    • 存储 key、value
    • 有 prev 和 next 指针
  2. LRUCache 类

    • 使用字典 self.cache 快速定位节点
    • 维护一个双向链表来记录使用顺序
    • 头部是最新的节点,尾部是最久未使用的节点
    • 使用 self.lock = threading.Lock() 来保护共享资源

✅ Python 实现代码如下:

python
深色版本
import threading

class Node:
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}  # key -> Node
        self.size = 0

        # 初始化双向链表的虚拟头尾节点
        self.head = Node()
        self.tail = Node()

        self.head.next = self.tail
        self.tail.prev = self.head

        self.lock = threading.Lock()  # 线程锁

    def _add_node(self, node):
        """将节点添加到头部"""
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node

    def _remove_node(self, node):
        """从链表中移除指定节点"""
        prev_node = node.prev
        next_node = node.next
        prev_node.next = next_node
        next_node.prev = prev_node

    def _move_to_head(self, node):
        """将节点移动到头部"""
        self._remove_node(node)
        self._add_node(node)

    def get(self, key):
        with self.lock:
            if key not in self.cache:
                return -1
            node = self.cache[key]
            self._move_to_head(node)  # 最近访问了,移到头部
            return node.value

    def put(self, key, value):
        with self.lock:
            if key in self.cache:
                node = self.cache[key]
                node.value = value
                self._move_to_head(node)
                return

            if self.size >= self.capacity:
                # 删除尾部节点(最久未使用)
                tail_prev = self.tail.prev
                self._remove_node(tail_prev)
                del self.cache[tail_prev.key]
                self.size -= 1

            new_node = Node(key, value)
            self._add_node(new_node)
            self.cache[key] = new_node
            self.size += 1

📦 使用示例

python
深色版本
# 示例:单线程测试
lru = LRUCache(2)
lru.put(1, 10)
lru.put(2, 20)
print(lru.get(1))  # 输出: 10
lru.put(3, 30)     # 容量已满,会淘汰 key=2
print(lru.get(2))  # 输出: -1 (已被淘汰)

# 示例:多线程测试
def worker(cache):
    for i in range(100):
        cache.put(i, i * 10)
        print(cache.get(i))

import threading

cache = LRUCache(10)
threads = [threading.Thread(target=worker, args=(cache,)) for _ in range(4)]

for t in threads:
    t.start()
for t in threads:
    t.join()

🔒 线程安全性说明

  • 所有对 cacheheadtailsize 的访问都包裹在 with self.lock: 中。
  • 这样可以防止多个线程同时修改数据结构导致的状态不一致或竞态条件。

🚀 性能优化建议(进阶)

如果你追求极致性能,还可以:

  • 将 Node 替换为 namedtuple 或者用 __slots__ 提升内存效率。
  • 使用更细粒度的锁(如分段锁),适用于高并发场景。
  • 替换为 OrderedDict(Python 3.7 前常用实现方式)。

如果你需要基于 OrderedDict 或者 functools.lru_cache 的版本,也可以告诉我,我可以继续补充。