关于网游服务器逻辑多线程处理的一些经验
2011-03-07 15:00
435 查看
众所周知,现在比较流行的网游服务端结构是逻辑单线程, 但是总会有一些其他原因促使你的架构不一定会按照最完美的情况来进行。下面就我上一个多线程逻辑的项目来说下一些要注意的事情和经验。
按场景分线程应该是比较合理的一个方法,可以这么理解:N个场景运行在同一个线程之内。而人物又是运行在场景之内的,那么问题就出来了,当A线程操作B线程玩家的指针的时候,而B玩家突然下线 ,导致这个指针被释放,则导致程序崩溃。最简单的方法是在所有需要在所有有可能操作他人指针的时候进行加锁处理,这样带来的锁的开销会很大,而且加锁处理的地方太多不容易管理。那么怎样才能避免这些锁操作呢,其实我们的目的很简单,只是为了让操作的对象始终有效,那么加入一个智能指针似乎就能解决这个问题。智能指针的原理是这样的,任何对该指针的引用拷贝 计数+1 ,不用了计数-1 当计数为0的时候销毁,那么这样只要对该指针有引用始终不会销毁此指针。有很多类似的问题,但是你发现的时候代码可能已经成型了,再做大规模的修改将相当不划算 ,那么有没有其他方式补救呢?这里提供一个比较粗俗的方法,在delete指针的时候不真正的delete ,而是将它放入一个延迟删除列表,比如15秒后删除,并且打一个不能使用的标记,这样在你服务器没有超强的延迟的时候是可以保证服务器安全的,即使你在引用的时候标记还没打上 当你引用的时候发生线程切换标志被打上了,然后线程切换回来你继续操作这个不应该使用的指针,这仅仅是个逻辑错误 而不会造成系统崩溃,但这只是一个补救的办法,真正的设计应该避免这个情况。
关于锁,一直是一个有争议的问题。传统的安全锁似乎效率太低,我们在很多时候对一个变量使用的时候多,修改的时间少,而不想每次使用的时候别人都不能使用,比如----世界聊天,我们只需要遍历玩家列表然后发包,而不想我发包的时候别人对玩家列表都不能操作,这样就引出了一个读写锁。原理是只要发生写操作只有写操作完毕才能读,而多个读操作之间不互斥只是计数++,读完计数--,计数为0可以写,网上也有很多例子,这里就不细说了。
还有一个细节,就是在函数开始的时候加锁,而函数中间有很多return的地方 这些地方都要释放,很容易不小心就遗漏了,那么这时候其实可以对锁进行封装 比如
class Guard
{
Public:
Guard(Mutex mutex): target(mutex) {target.lock();}
~Guard(){target.unlock();}
....
}
这样我们使用锁的时候只需要 Guard guard(lock); 简单的定义一个局部变量即可 ,无需手动释放。
在处理网络数据的时候我们一般将接收到的数据按完整的包打到一个缓存里,处理线程有空的时候去缓存取数据,而不是直接操作网络缓冲区,这里是操作最频繁的地方,读写都要操作缓存数据区,那么必然也存在线程安全问题,也就是锁必然而然的也将存在,那么这里有没有什么方法优化呢?答案是肯定的。我们设计一个简单的模式 做一个双缓冲处理,一个 数据队列,写数据的时候正常流程,读数据的时候加锁把头指针和大小赋给一个新的队列,而原队列大小清空重新分配空间来存写入的数据,这样我们在读的时候只要简单的加一次锁然后赋个值就能进行无锁操作。
~~大概能想到的就是这么多了 ,写出来也算是对自己前段时间项目的一个总结
按场景分线程应该是比较合理的一个方法,可以这么理解:N个场景运行在同一个线程之内。而人物又是运行在场景之内的,那么问题就出来了,当A线程操作B线程玩家的指针的时候,而B玩家突然下线 ,导致这个指针被释放,则导致程序崩溃。最简单的方法是在所有需要在所有有可能操作他人指针的时候进行加锁处理,这样带来的锁的开销会很大,而且加锁处理的地方太多不容易管理。那么怎样才能避免这些锁操作呢,其实我们的目的很简单,只是为了让操作的对象始终有效,那么加入一个智能指针似乎就能解决这个问题。智能指针的原理是这样的,任何对该指针的引用拷贝 计数+1 ,不用了计数-1 当计数为0的时候销毁,那么这样只要对该指针有引用始终不会销毁此指针。有很多类似的问题,但是你发现的时候代码可能已经成型了,再做大规模的修改将相当不划算 ,那么有没有其他方式补救呢?这里提供一个比较粗俗的方法,在delete指针的时候不真正的delete ,而是将它放入一个延迟删除列表,比如15秒后删除,并且打一个不能使用的标记,这样在你服务器没有超强的延迟的时候是可以保证服务器安全的,即使你在引用的时候标记还没打上 当你引用的时候发生线程切换标志被打上了,然后线程切换回来你继续操作这个不应该使用的指针,这仅仅是个逻辑错误 而不会造成系统崩溃,但这只是一个补救的办法,真正的设计应该避免这个情况。
关于锁,一直是一个有争议的问题。传统的安全锁似乎效率太低,我们在很多时候对一个变量使用的时候多,修改的时间少,而不想每次使用的时候别人都不能使用,比如----世界聊天,我们只需要遍历玩家列表然后发包,而不想我发包的时候别人对玩家列表都不能操作,这样就引出了一个读写锁。原理是只要发生写操作只有写操作完毕才能读,而多个读操作之间不互斥只是计数++,读完计数--,计数为0可以写,网上也有很多例子,这里就不细说了。
还有一个细节,就是在函数开始的时候加锁,而函数中间有很多return的地方 这些地方都要释放,很容易不小心就遗漏了,那么这时候其实可以对锁进行封装 比如
class Guard
{
Public:
Guard(Mutex mutex): target(mutex) {target.lock();}
~Guard(){target.unlock();}
....
}
这样我们使用锁的时候只需要 Guard guard(lock); 简单的定义一个局部变量即可 ,无需手动释放。
在处理网络数据的时候我们一般将接收到的数据按完整的包打到一个缓存里,处理线程有空的时候去缓存取数据,而不是直接操作网络缓冲区,这里是操作最频繁的地方,读写都要操作缓存数据区,那么必然也存在线程安全问题,也就是锁必然而然的也将存在,那么这里有没有什么方法优化呢?答案是肯定的。我们设计一个简单的模式 做一个双缓冲处理,一个 数据队列,写数据的时候正常流程,读数据的时候加锁把头指针和大小赋给一个新的队列,而原队列大小清空重新分配空间来存写入的数据,这样我们在读的时候只要简单的加一次锁然后赋个值就能进行无锁操作。
~~大概能想到的就是这么多了 ,写出来也算是对自己前段时间项目的一个总结
相关文章推荐
- Godaddy服务器上关于ASP.NET网站建设一些经验 - 断点续传下载 (二)
- 关于不同服务器平台开发时EOF的处理经验
- 关于一些业务逻辑的处理技巧
- 关于订单支付成功后一些逻辑的处理
- 关于用DELPHI开发服务器软件中的一些经验
- 关于大数据的处理的一些经验
- Godaddy服务器上关于ASP.NET网站建设一些经验 - 防SQL注入攻击(三)
- Godaddy服务器上关于ASP.NET网站建设一些经验(一)
- 关于JSP编译流程,和Vector处理的一些小经验
- 一些关于网游服务器的东东
- 关于微软的OWA服务器搭建的一些心得
- 关于cometd的一些经验总结-js端
- 关于oracle采用pivot函数列转行后一些列的值为null处理
- 关于表格动态添加行并处理相关表单元素的一些修改----优化for重用(1)
- 关于自适应屏幕方向和大小的一些经验
- 处理Clob数据(转)关于oracle中大对象处理的一些方法和实例
- 一些关于中文乱码问题的一些解决方案和经验
- ABAP--关于ABAP流程处理的一些命令的说明(stop,exit,return,check,reject)
- 关于宏的一些处理
- 关于访问远程服务器的一些基本操作