Skip to main content
 首页 » 编程设计

c#之关于锁性能/使用的 2 个问题

2024年01月13日31telwanggs

在服务器应用程序上,我需要为每个连接的客户端分配一个唯一的 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 添加到池中。