如何从 Erlang/Elixir 中的 ets 集中选择一个随机元素?
Posted
技术标签:
【中文标题】如何从 Erlang/Elixir 中的 ets 集中选择一个随机元素?【英文标题】:How do I select a random element from an ets set in Erlang/Elixir? 【发布时间】:2019-05-11 15:26:06 【问题描述】:我需要在 ets 集中跟踪大量进程,然后随机选择单个进程。所以我创建了这样的集合:
:ets.new(:pid_lookup, [:set, :protected, :named_table])
那么为了争论,让我们在其中粘贴self()
1000 次:
Enum.map 1..1000, fn x -> :ets.insert(:pid_lookup, x, self()) end
现在我需要随机选择一个。我知道我可以使用:ets.lookup(:pid_lookup, :rand.uniform(1000))
随机选择一个,但是如果我事先不知道集合的大小(在上述情况下为 1000)怎么办?
如何找出 ets 集的大小?和/或是否有更好的方法从 ets 数据结构中选择随机 pid?
【问题讨论】:
您可以使用:ets.info(tab, :size)
来获取表格的大小。不过,我真的不知道从中获取随机元素的最佳选择。从集合中获取随机 pid 的用例是什么?
@JustinWood 我只是对 genservers 进行基准测试。我正在创建其中的 1000 个,然后我希望每个都向另一个随机发送消息多次,并查看使用各种消息大小需要多长时间。但是当我创建 Genservers 时,它们会启动,因此其他 genservers 在它们开始发送时可能还没有启动。所以我需要知道在基准测试的初始阶段创建了多少。
嗯,1000 当然是任意的。我将根据各种数字检查性能。我本质上是想了解消息传递的速度。
如果你只是想测试消息传递,为什么不启动所有的 gen 服务器然后开始发送消息?
嗯,我确实想知道如何在 ets 表中随机选择一些东西来进行我所做的一堆统计工作。但是要直接回答您的问题,当我启动 1000 个 Genserver 时,第一个将在最后一个启动之前开始发送。因此,我不能只从 1000 个中选择一个随机数。我需要知道已经旋转了多少,为此我需要知道 ETS 表的大小。
【参考方案1】:
如果键是序号
tab = :ets.new(:tab, [])
Enum.each(1..1000, & :ets.insert(tab, &1, :value))
size = :ets.info(tab, :size)
# size = 1000
value_picked_randomly = :ets.lookup(tab, Enum.random(1..1000))
:ets.info(tab, :size)
返回表的大小;这是在给定表上插入的许多记录。
first = :ets.first(tab)
:ets.lookup(tab, first)
func = fn key->
if function_that_may_return_true() do
key = case :ets.next(tab, key) do
:'$end_of_table' -> throw :reached_end_of_table
key -> func.(key)
end
else
:ets.lookup(tab, key)
end
end
func.()
func
将遍历 ets 表并返回一个随机值。
这将非常耗时,因此对于具有大量记录的表来说,这不是一个理想的解决方案。
【讨论】:
【参考方案2】:据我从 cmets 了解到,这是一个 XY Problem。
您本质上需要的是追踪不断变化的列表并随机选取其中一个元素。一般的ETS
和特别的:ets.set
绝不是为了查询大小。它们有不同的用途。
在您的监督树中生成一个 Agent
,保存已启动服务器的 PID 列表,并使用 Kernel.length/1
查询其大小,如果列表不正确,甚至使用 Enum.random/1
> 巨大(后者遍历整个可枚举得到一个随机元素。)
【讨论】:
只有当您不担心对您的代理进行 DDOS 攻击时,这才是一个解决方案。我特别避免了涉及单进程瓶颈的解决方案。 1000 只是一个例子。 cmets 进一步声明我正在进行基准测试。因此,可以合理地假设要跟踪的数量可能会变大几个数量级。因此,ETS。这个问题真的很清楚。我没有要求别人对我的架构进行第二次猜测,我只是询问了有关 ETS 的具体问题,即如何找到大小或如何随机抽样。 我 102% 肯定Agent
在这种情况下会做得很好,但如果你坚持使用 ETS,只需跟踪数字就是单独的 ETS。插入 PIDs ETS 后,还要增加计数器。以上是关于如何从 Erlang/Elixir 中的 ets 集中选择一个随机元素?的主要内容,如果未能解决你的问题,请参考以下文章
Elixir/Erlang 中的命名函数是不是有等效于 __MODULE__ 的方法?