在服务器应用程序上,我需要为每个连接的客户机分配一个唯一的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;
}
}我的第一个问题:是否可以更有效地完成,我的意思是更好的性能?我在这里读到,我们应该学会编写没有锁的代码。我也读过那里的一些原子操作,还有其他选择。第二个问题:如果我想锁定整个类,以便不允许在其中进行任何更改,该怎么办?例如:一个客户端将更新第二个客户端的数据,我可以锁定整个第二个客户端类,它是绝对阻塞的吗?我仍然认为" lock“只能确保代码片段中的代码同时只由一个线程输入,所以我不知道"lock(client2)”是否会导致该类中的任何内容在这个锁被释放之前都不能被更改。
发布于 2009-12-21 21:18:33
锁通常是让事情变得正确的最简单方法,这一点非常重要。通常情况下,有没有更有效的方法并不重要,只要你有清晰的代码,并且执行得足够好。
然而,这里一种性能更好的方法是生成一个随机的GUID,或者如果您确实想要重用ID,那么可以有一个由未使用的ID组成的“池”(例如LinkedList)。然后,您可以非常快速地从池中获取数据,并在完成后将ID返回到池中(再次快速返回)。
或者,如果你真的只需要一个整数,并且它不一定是一个小整数,你可以有一个从0开始的静态变量,你可以每次递增它-如果你愿意,你可以不使用Interlocked.Increment来锁定。我怀疑您是否会用完64位整数,例如:)
至于你的第二个问题:是的,锁是建议的。如果在更改任何字段(并且字段是私有的)之前,类中的所有内容都取出相同的锁,那么就可以防止其他代码行为异常……但每一段代码都需要取出锁。
编辑:如果你真的只需要一个整数,我仍然建议你只使用Interlocked.Increment -即使你的流量增加了1000倍,你也可以使用64位整数。但是,如果您想重用ID,那么我建议您创建一个新类型来表示“池”。给它一个已经创建了多少的计数器,这样如果你用完了,你就可以分配一个新的项目。然后只需将可用的文件存储在Queue<int>、LinkedList<int>或Stack<int>中(使用哪种文件并不重要)。假设您可以信任您自己的代码能够合理地返回ID,那么您可以将API简化为:
int AllocateID()
void ReturnID(int id)AllocateID将检查池是否为空,如果是,则分配新的ID。否则,它只会从池中删除第一个条目并返回该条目。ReturnID只会将指定的ID添加到池中。
发布于 2009-12-21 21:20:01
您在扫描阵列时被锁定。
你最好有两个堆栈。一个是使用空闲ID的,另一个是使用ID的。这样你就可以弹出第一个堆栈中的一个,并将其推送到第二个堆栈中。
这样,你锁定的时间就会短得多。
发布于 2009-12-21 21:47:42
You can allocate state on thread local memory.线程本地内存是线程安全的(只要你不传递指针)。
您可以使用两个整数来生成唯一数字,并且只有一个是同步数字。
整数1:表示线程的递增整数,每次初始化线程时都会生成一个新的数字(这应该是很少见的事件)。
Integer2:在线程初始化时,这个整数从0开始。
您将使用这两个整数--它们存储在线程本地内存中--作为唯一的整数,整数2将正常递增(解锁)。
这样,唯一整数的生成绝对是线程安全的--也就是说,您不必使用原子CPU指令-- Interlocked.increment (这确实会导致硬件级别的性能损失)。
-- edit :缓存一致性--
from :
缓存一致性
为了减少内存访问所需的时间,使用了不同的缓存:最近访问的内存在CPU缓存中复制,这比普通内存快得多。将来对同一地址的访问将使用保存在缓存中的数据,从而减少获取时间。对称多处理( SMP )系统中存在这样的问题:当多个处理机拥有自己的高速缓冲存储器时,当一个处理机改变存储区域中的变量,被多个处理机同时使用时,它实际上改变了位于高速缓冲存储器中的变量的自身副本,而共享变量仍然具有原始值。这个问题不能通过在共享变量上使用volatile关键字来解决,因为这将只保证写入内存指令将出现在结果程序中,但仍然没有指定与缓存相关的操作。当然,可以禁用CPU缓存,将内存映射为无缓存( VirtualAlloc() Win32 API函数中PAGE_NOCACHE保护标志),但随着速度的显著减慢,这会带来一些限制:例如,互锁指令可能会在无缓存内存上引发硬件异常。
存储在多个处理器的高速缓存中的SMP系统数据在所有高速缓存中的正确工作的应该是相同的。这意味着CPU高速缓存必须在硬件级别同步(保持一致)**。但重要的是要注意,高速缓存同步(高速缓存一致性流量流)是与程序执行异步进行的:**当一个CPU更改共享变量的值时,另一个CPU临时观察旧值。这意味着CPU继续执行而不等待高速缓存一致性操作完成。此外,如果两个变量(a然后b)被第一个CPU改变,另一个CPU可以观察到b比a更早地改变。
在这一点上,互锁指令有很大的不同。确切地说,互锁指令是在锁定总线下直接在物理存储器上进行某些操作的命令。这意味着高速缓存不一致不会影响仅通过互锁指令访问共享变量的程序(请注意,读取变量和写入变量的两个进程都应使用互锁指令)。
-- edit :进一步分类:--
在您当前的设计下,互锁增量是indeed your best bet,但它远不是理想的。您的CPU有一个非常快的片内缓存(通常与CPU的速度相同)。如果你的线程有本地内存,它会被拉到你的线程所在的CPU中,这意味着你的CPU不需要去主存,它可以全速飞行。
如果使用互锁增量,CPU将不得不
锁定总线上的32位word.
你不需要这个也行。我可能看起来有点学究,因为开销可能只有be a 100% decrease in relative performance。然而,在一个有4个物理CPU和16个核心的工业服务器应用程序中,这个UID生成器在每次请求时都会触发...相信我,你的公交车会完蛋的。微优化是编程中的一个重要领域,特别是在我们现在进行水平扩展的时候。
https://stackoverflow.com/questions/1940044
复制相似问题