在服务器应用程序上,我需要为每个连接的客户端分配一个唯一的 ID,所以我是这样做的:
private short GetFreeID()
{
lock (this.mUsedPlayerIDsSynchronization)
{
for (short I = 1; I < 500; I++)
{
if (ClientIDPool[I] == false)
{
ClientIDPool[I] = true;
return I;
}
}
return -1;
}
}
我的第一个问题:是否可以更高效地完成,我的意思是性能更好?我在这里读到我们应该学会写没有锁的代码。我还在那里阅读了一些原子操作,还有其他选择。 第二个问题:如果我想锁定整个类(class)以不允许在其中进行任何更改怎么办?例如:一个客户端将更新第二个客户端的数据,我可以锁定整个第二个客户端类以使其完全被阻止吗?我仍然认为“锁定”只会确保当时只有一个线程输入其片段中的代码,所以我不知道“锁定(client2)”是否会导致该类中的任何内容都无法更改,直到锁定发布。
最佳答案
锁通常是最简单的正确处理方式,这一点非常重要。通常,是否有更有效的做事方式并不重要,只要您拥有清晰的代码并且它的性能足够好即可。
然而,这里的一种更高效的方法是生成一个随机 GUID,或者如果您确实想要重用 ID,则有一个未使用的“池”(例如 LinkedList
)身份证。然后,您可以非常快速地从池中取出 ID,并在完成后(再次快速)将 ID 返回到池中。
或者,如果您真的只需要一个整数并且它没有是一个小整数,您可以有一个从 0 开始并且每次都递增的静态变量 - 你可以在不使用 Interlocked.Increment
锁定的情况下执行此操作如果你愿意的话。我怀疑您是否会用完 64 位整数,例如 :)
关于您的第二个问题:是的,锁是建议性的。如果类中的所有内容在更改任何字段(并且字段是私有(private)的)之前都取出了相同的锁,那么这可以防止其他代码行为不当......但是每一位代码确实需要取出锁.
编辑:如果你真的只需要一个整数,我仍然建议只使用 Interlocked.Increment
- 即使您的流量增加了 1000 倍,您也可以使用 64 位整数来代替。但是,如果您想重用 ID,那么我建议创建一个新类型来表示“池”。给那个一个已经创建了多少的计数器,这样如果你用完了你可以分配一个新的项目。然后将可用的存储在 Queue<int>
中, LinkedList<int>
或 Stack<int>
(使用哪种并不重要)。假设您可以相信自己的代码能够明智地返回 ID,您可以使 API 变得如此简单:
int AllocateID()
void ReturnID(int id)
AllocateID
将检查池是否为空,如果是,则分配一个新 ID。否则,它只会从池中删除第一个条目并将其返回。 ReturnID
只会将指定的 ID 添加到池中。
关于c# - 关于锁性能/使用的 2 个问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1940044/