幀調(diào)度器
幀調(diào)度器
我們?cè)趯憫?zhàn)斗侍筛,或者寫房間類型的時(shí)候就需要一個(gè)按幀調(diào)度的調(diào)度器额获。
1. 抽象一個(gè)被調(diào)度器調(diào)度的單元
/// <summary>
/// 可更新對(duì)象
/// </summary>
public interface Updatable
{
/// <summary>
/// Id標(biāo)識(shí)
/// </summary>
public int Id { get; set; }
/// <summary>
/// 周期性u(píng)pdate
/// </summary>
/// <param name="dt"></param>
void Update(int dt);
}
2. 定義一個(gè)調(diào)度器
internal class UpdateExecutor
{
/// <summary>
/// 需要被調(diào)度的對(duì)象
/// </summary>
private List<Updatable>[] _updatables;
/// <summary>
/// 日志
/// </summary>
private Logger _log;
/// <summary>
/// 鎖對(duì)象
/// </summary>
private object _lock;
/// <summary>
/// 當(dāng)前游標(biāo)
/// </summary>
private int _cursor;
/// <summary>
/// 運(yùn)行的線程
/// </summary>
public System.Threading.Thread Thread { get; set; }
/// <summary>
/// 構(gòu)造函數(shù)
/// </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; // 開始時(shí)間
var interval = RoomScheduler.Interval;
while (true)
{
var curr = TimeUtil.FastDateTimeNow; // 當(dāng)前時(shí)間
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. 定義調(diào)度器
/// <summary>
/// 調(diào)度器
/// </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; // 初始化標(biāo)志
private UpdateExecutor[] _executors; // 執(zhí)行器
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>
/// 加入調(diào)度
/// </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>
/// 移除調(diào)度
/// </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);
}
}