GenServer中创建ETS公共表后执行操作报错:表标识符未指向现有ETS表
GenServer中创建ETS公共表后执行操作报错:表标识符未指向现有ETS表
我来帮你排查这个问题,看了你的代码后,发现几个关键问题导致了这个报错:
1. 未给ETS表设置命名,导致通过原子名找不到表
你的init函数里调用:ets.new(:my_table, [:duplicate_bag, :public])时,这里的:my_table只是你定义的变量名,并不是ETS表的实际注册名称。默认情况下,:ets.new会返回一个整数类型的表标识符,而不会把传入的原子作为表名注册。如果想通过:my_table这个原子直接引用表,必须在创建时添加:named_table选项。
修改后的init代码:
def init(_opts \\ []) do # 添加 :named_table 选项,让表可以通过 :my_table 原子直接引用 :ets.new(:my_table, [:duplicate_bag, :public, :named_table]) {:ok, %{}} end
2. ETS操作函数存在笔误
你代码里写的:ets.thing应该是笔误吧?ETS并没有这个函数,常见的操作比如插入数据应该用:ets.insert,比如:
def add_player(zone_id, socket_id) do :ets.insert( :my_table, {zone_id, [socket_id]} # 这里替换成你实际需要的键值对 ) end
另外要确保some_key和my_uuid是已定义的变量,否则会触发其他错误。
3. 确认GenServer进程存活状态
虽然你确认init已经执行,但如果GenServer进程意外崩溃,即使是public类型的ETS表也会被销毁(因为ETS表默认和创建它的进程绑定,进程退出时表会自动删除)。如果需要进程退出后表仍保留,可以添加:persistent选项,但这个选项要谨慎使用,避免内存泄漏。
备选方案:将表标识符存在GenServer状态中
如果你不想使用命名表,也可以把创建表时返回的整数标识符存在GenServer的状态里,通过GenServer的消息传递来操作表:
def init(_opts \\ []) do table = :ets.new(:my_table, [:duplicate_bag, :public]) {:ok, %{table: table}} # 把表ID存入状态 end # 对外暴露的调用接口 def add_player(zone_id, socket_id) do GenServer.call(__MODULE__, {:add_player, zone_id, socket_id}) end # 处理call消息的回调函数 def handle_call({:add_player, zone_id, socket_id}, _from, %{table: table} = state) do :ets.insert(table, {zone_id, [socket_id]}) {:reply, :ok, state} end
这种方式直接使用创建时返回的表ID,也能避免找不到表的问题。
总结一下,最快速的解决方法就是给ETS表添加:named_table选项,同时修正ETS操作的函数名。
备注:内容来源于stack exchange,提问作者bezzoon
相关产品推荐
相关产品推荐

