程序员C#

C#实现一个帧调度器

2023-02-18  本文已影响0人  Aodota

帧调度器

帧调度器 我们在写战斗,或者写房间类型的时候就需要一个按帧调度的调度器。

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);
    }
}
上一篇 下一篇

猜你喜欢

热点阅读