帧调度器
帧调度器
我们在写战斗,或者写房间类型的时候就需要一个按帧调度的调度器。
1. 抽象一个被调度器调度的单元
/// <summary>
/// 可更新对象
/// </summary>
public interface Updatable
{
/// <summary>
/// Id标识
/// </summary>
public int Id { get; set; }
/// <summary>
/// 周期性update
/// </summary>
/// <param name="dt"></param>
void Update(int dt);
}
2. 定义一个调度器
internal class UpdateExecutor
{
/// <summary>
/// 需要被调度的对象
/// </summary>
private List<Updatable>[] _updatables;
/// <summary>
/// 日志
/// </summary>
private Logger _log;
/// <summary>
/// 锁对象
/// </summary>
private object _lock;
/// <summary>
/// 当前游标
/// </summary>
private int _cursor;
/// <summary>
/// 运行的线程
/// </summary>
public System.Threading.Thread Thread { get; set; }
/// <summary>
/// 构造函数
/// </summary>
public UpdateExecutor(Logger log)
{
_updatables = new List<Updatable>[2];
_updatables[0] = new List<Updatable>();
_updatables[1] = new List<Updatable>();
_log = log;
_lock = new object();
_cursor = 0;
}
public void Execute()
{
var start = TimeUtil.FastDateTimeNow; // 开始时间
var interval = RoomScheduler.Interval;
while (true)
{
var curr = TimeUtil.FastDateTimeNow; // 当前时间
var frameDt = (int)(curr - start).TotalMilliseconds; // 帧间隔
start = curr;
RunFrame(frameDt);
var execTime = (int)(TimeUtil.FastDateTimeNow - start).TotalMilliseconds;
if (execTime < interval)
{
System.Threading.Thread.Sleep(interval - execTime);
}
}
}
private void RunFrame(int dt)
{
List<Updatable> updatables;
lock(_lock)
{
updatables = _updatables[_cursor];
}
foreach(var updatable in updatables)
{
try
{
updatable.Update(dt);
}
catch (Exception e)
{
_log.Error(e, "run frame error, roomId:{0}", updatable.Id);
}
}
}
public void Schedule(Updatable updatable)
{
lock (_lock)
{
var newList = new List<Updatable>();
newList.AddRange(_updatables[_cursor]);
newList.Add(updatable);
var cursor = 1 - _cursor;
_updatables[cursor] = newList;
_cursor = cursor;
}
}
public void UnSchedule(Updatable updatable)
{
lock (_lock)
{
var newList = new List<Updatable>();
newList.AddRange(_updatables[_cursor]);
newList.Remove(updatable);
var cursor = 1 - _cursor;
_updatables[cursor] = newList;
_cursor = cursor;
}
}
}
3. 定义调度器
/// <summary>
/// 调度器
/// </summary>
public class Scheduler
{
/// <summary>
/// Logger
/// </summary>
private static Logger Log = LogFactory.GetLog("com.will.gameroom");
/// <summary>
/// 帧间隔
/// </summary>
public static int Interval { get; set; } = 1000 / 1;
/// <summary>
/// 单例
/// </summary>
public static Scheduler Instance { get; } = new();
private int _initFlag; // 初始化标志
private UpdateExecutor[] _executors; // 执行器
private Scheduler()
{
}
/// <summary>
/// 初始化
/// </summary>
/// <param name="threadNum"></param>
public void Init(int threadNum = 2)
{
if (Interlocked.CompareExchange(ref _initFlag, 1, 0) == 0)
{
_executors = new UpdateExecutor[threadNum];
for (var i = 0; i < threadNum; i++)
{
_executors[i] = new UpdateExecutor(Log);
var currThread = new System.Threading.Thread(_executors[i].Execute);
if (!currThread.IsAlive)
{
currThread.Start();
}
_executors[i].Thread = currThread;
}
}
}
/// <summary>
/// 加入调度
/// </summary>
/// <param name="updatable"></param>
public void Schedule(Updatable updatable)
{
var mod = updatable.Id % _executors.Length;
_executors[mod].Schedule(updatable);
Log.Info("room#schedule#{0}", updatable.Id);
}
/// <summary>
/// 移除调度
/// </summary>
/// <param name="updatable"></param>
public void UnSchedule(Updatable updatable)
{
var mod = updatable.Id % _executors.Length;
_executors[mod].UnSchedule(updatable);
Log.Info("room#unschedule#{0}", updatable.Id);
}
}