如何在Elixir函数中实现值缓存?覆盖C++静态变量、数据库缓存场景
场景1:计数器函数的等价实现
C++中函数内部的静态变量本质是跨调用持久化的可变状态,Elixir作为无共享可变状态的函数式语言,没有原生支持这类语法,最符合语言设计规范的实现是用Agent进程维护计数器状态:
defmodule Counter do use Agent # 启动计数器进程,初始值对应C++代码中的static int i = 5 def start_link(_opts) do Agent.start_link(fn -> 5 end, name: __MODULE__) end def func do # 原子性更新状态并返回最新值 i = Agent.get_and_update(__MODULE__, fn state -> new_val = state + 1 {new_val, new_val} end) IO.puts("i is #{i}") end end # 调用测试 Counter.start_link([]) Counter.func() # 输出i is 6 Counter.func() # 输出i is 7
该实现线程安全,状态由独立进程维护,避免了共享可变状态带来的并发问题。
场景2:数据库查询缓存的等价实现
原有代码存在两个问题:一是每次函数调用都会重新初始化空Map作为缓存,二是Elixir的词法作用域规则下,if代码块内部的变量赋值不会影响外部作用域的同名变量。针对缓存需求有两种常用实现方案:
方案1:全局跨进程缓存(ETS实现)
如果需要在多进程间共享缓存,用Erlang内置的ETS表实现性能最优:
defmodule ElixirTesting do def start_link(_opts) do # 创建全局命名的ETS缓存表,支持并发读 :ets.new(:db_cache, [:set, :public, :named_table]) Agent.start_link(fn -> :ok end, name: __MODULE__) end def test_static_function_variable do IO.puts(get_value_from_db_cache("A")) IO.puts(get_value_from_db_cache("B")) IO.puts(get_value_from_db_cache("A")) true end defp get_value_from_db_cache(key) do case :ets.lookup(:db_cache, key) do # 命中缓存直接返回 [{^key, value}] -> value # 未命中则查询数据库写入缓存后返回 [] -> value = get_value_directly_from_db(key) :ets.insert(:db_cache, {key, value}) value end end @db_data %{ "A" => 3, "B" => 4, "C" => 5, "D" => 6 } defp get_value_directly_from_db(key) do IO.puts("Inside get_value_directly_from_db for key #{key}") @db_data[key] end end # 测试调用 ElixirTesting.start_link([]) ElixirTesting.test_static_function_variable()
运行后输出如下,可见第二次查询key"A"时不会触发数据库访问:
Inside get_value_directly_from_db for key A 3 Inside get_value_directly_from_db for key B 4 3
方案2:纯函数实现(无额外依赖)
如果不需要跨调用/跨进程共享缓存,可以将缓存作为参数在函数间传递,完全符合纯函数式编程规范:
defmodule PureElixirTesting do def test_static_function_variable do cache = %{} {val_a, cache} = get_value_from_db_cache("A", cache) IO.puts(val_a) {val_b, cache} = get_value_from_db_cache("B", cache) IO.puts(val_b) {val_a2, _} = get_value_from_db_cache("A", cache) IO.puts(val_a2) true end defp get_value_from_db_cache(key, cache) do if Map.has_key?(cache, key) do {cache[key], cache} else value = get_value_directly_from_db(key) {value, Map.put(cache, key, value)} end end @db_data %{ "A" => 3, "B" => 4, "C" => 5, "D" => 6 } defp get_value_directly_from_db(key) do IO.puts("Inside get_value_directly_from_db for key #{key}") @db_data[key] end end
该实现不需要启动额外进程,适合单进程内的批量查询场景。
内容的提问来源于stack exchange,提问作者cppProgrammer
相关产品推荐
相关产品推荐

