我正在尝试找到一种有效的确定性方法,以一种无限期运行的方式分配32位句柄。一个简单的递增计数器将无法工作,因为它最终会循环。不可能扩展到64位,因为这些值将用于期望32位值的网络协议中。
它适用于实时系统,因此必须具有确定性和快速性。虽然它最终会出现在SQLite数据库中,但我不能在循环后对每个键进行强力测试,例如...
我认为我需要的是某种范围树,它知道所有先前分配的句柄(在启动时填充这个很好)。这似乎是一种常见的(ish)问题,但它不是通过boost或STL解决的问题。
任何指针?
编辑:一些额外的澄清。我希望在任何时候都能在系统中拥有200k活动句柄的顺序。句柄随机删除。
答案 0 :(得分:3)
您不能分配超过2 ^ 32。但是如果它们被释放,您可以重新分配使用过的句柄,问题是跟踪空闲句柄。
树是存储空闲句柄的好方法。每个节点都有一个最低和最高句柄,左子树包含小于最低句柄的句柄,右子树包含大于最高句柄的句柄。
一个例子是:
6-9
/ \
2 15
/ \
0 4
如果释放句柄,它将存储在树中。例如,如果释放10,则树看起来像:
6-10
/ \
2 15
/ \
0 4
如果释放了句柄5,您可以选择优化树,因为4可以添加到5-10节点中:
5-10
/ \
2 15
/ \
0 4
要:
4-10
/ \
2 15
/
0
分配句柄,搜索带有1个句柄的叶节点并将其从树中删除。如果没有带有1个句柄的叶子,只需使用叶子并减少未连接到父对象的一侧:
4-10
/
1-2
在上面的示例中,我们分配1而不是2,因为如果释放3,则可以将其与4组合,并且希望保持节点数尽可能低。
下面是伪代码算法。有些部分留给读者:
Node = ( int lowest, highest; [Node] left, right)
Node.Allocate()
if TryFindLonelyLeaf(handle) return handle;
else
return FindLeafHandle(NULL);
Node.TryFindLonelyLeaf(handle)
if (left == NULL & right == NULL)
if (lowest == highest)
handle == lowest;
return TRUE;
else
return FALSE;
if (left != NULL & left.TryFindLonelyLeaf(handle))
if (left.lowest == handle)
left == NULL; // Free node
return TRUE;
elsif (right != NULL & right.TryFindLonelyLeaf(handle))
if (right.lowest == handle)
right = NULL; // Free node
return TRUE;
else
return FALSE;
Node.FindLeafHhandle(parent)
if (left == NULL & right == NULL)
if (parent == NULL | parent.right == this)
handle = lowest;
lowest++;
else
handle = highest;
highest--;
return handle;
else if (left != NULL)
return left.FindLeafHandle();
else // right != NULL
return right.FindLeafHandle();
Node.DeAllocate(handle)
Assert(handle<lowest or handle>highest);
if (handle == lowest-1)
lowest = CompactLeft(handle);
elsif (handle == lowest+1)
highest = CompactRight(handle);
elsif (handle<lowest)
if (left == NULL)
left = (handle, handle, NULL, NULL);
else
left.DeAllocate(handle);
elsif (handle>highest)
if (right == NULL)
right = (handle, handle, NULL, NULL);
else
right.DeAllocate(handle);
else ERROR
Node.CompactLeft(handle)
if (highest == handle-1)
handle = lowest;
// deallocate node and replace with left subtree
return handle;
elsif (right != NULL)
return right.CompactLeft(handle)
else
return handle;
Node.CompactRight(handle)
if (lowest == handle+1)
handle = highest;
// deallocate node and replace with right subtree
return handle;
elsif (left != NULL)
return left.CompactRight(handle)
else
return handle;
答案 1 :(得分:3)
如果内存不是问题,那么您可以维护一个免费句柄列表。释放一个时,将其添加回空闲列表的末尾。
一开始,您可以将所有ID添加到空闲列表中,但效率很低。
您可以做的优化是保持一个值,即可用的最小ID,以及空闲列表。因此,当列表为空时,您将添加一些id(从您维护的最小id值开始)到空闲列表并更新最小id值。
答案 2 :(得分:0)
如果这个问题只是“我怎样才能快速,安全地计算一个独特的,当前未使用过的数字”,那么一个位表会给你这个。
对于200K唯一编号的顺序,200.000 / 8 =所需的字节数= 25000,这只是为了跟踪的25KB内存。
当然,如果您需要在内存中跟踪与使用中句柄相关的数据,那么您还需要其他内容。
另一种解决方案,可能是更快地获得新句柄,将保留一堆未使用的句柄。每次需要一个手柄时,从堆栈中弹出一个。
您可以使用设定的数字为堆栈设定种子,但算法也是如此,如果您尝试从空堆栈中弹出一个值,您只需通过递增一个不断增加的值来生成一个新值。
因为你说你在任何给定的时间都会有大约200K的句柄,所以这个堆栈永远不会比握住那么多的句柄大,所以你可以用数组轻松处理它。 200K 32位堆栈将消耗800.000字节,大约781KB的内存。