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

标签 c# multithreading locking

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

相关文章:

c# - C# 中的数字类型后缀

c++ - 英特尔 tbb parallel_for : pass class member function with parameters?

c# - 如果作为单独的任务启动,有没有办法取消 Parallel.ForEach 循环

c# - 当存在上下文切换时,获取锁的开销是否取决于操作系统计时器分辨率?

c++ - shared_timed_mutex 在 OS X 10.11.2 上不可用?

windows - 释放 Windows 文件共享锁

C#:运行进程然后使用按钮终止它

c# - 在 C# 错误 "String cannot be of zero length. Parameter name: oldValue"中使用 linq to xml 创建动态 xml 文件

c# - File.Exists——不想创建新文件

java - EJB 池与线程安全和@PreDestroy