对应版本:
2020-9-14 a9b44b68ecc3c325be81f71217712e07266df58b commit
TimerComponent组件
使用Task.Delay会占用一个线程池线程,性能不理想, 因此使用此组件来代替。
成员
/// <summary>
/// key :计时器InstanceId
/// value:实现ITimer接口的计时器对象)
/// </summary>
private readonly Dictionary<long, ITimer> timers = new Dictionary<long, ITimer>();
/// <summary>
/// 内部实现是红黑树排序的字典SortedDictionary (T, List K)
/// T :目标时间,
/// Lisk K:计时器InstanceId的列表
/// ( 同一时间可能有很多个计时器 )
/// </summary>
public readonly MultiMap<long, long> TimeId = new MultiMap<long, long>();

Update逻辑

参数区别与作用
<tillTime> vs <time>
public async ETTask<bool> WaitTillAsync(long tillTime, ETCancellationToken cancellationToken)
public async ETTask<bool> WaitTillAsync(long tillTime)
TimeHelper.Now() > tillTime
直到某个时间
public async ETTask<bool> WaitAsync(long time, ETCancellationToken cancellationToken)
public async ETTask<bool> WaitAsync(long time)
long tillTime = TimeHelper.Now() + time;
TimeHelper.Now() > tillTime
n毫秒后
<ETCancellationToken>
外部调用方法传入取消令牌
会在创建计时器时在令牌上注册取消方法, 移除对应计时器
cancellationToken.Register(() =>
{
// 用对象无法表示对象的唯一性,可能被释放又被别人重用了,用instanceId才能表示唯一性
if (instanceId != timer.InstanceId)
{
return;
}
// 传入false表示手动取消计时器后回调
timer.Run(false);
// 又移除一次?
// 在timer.Run(false);中已经调用GetParent<TimerComponent>().Remove(this.Id);了
this.Remove(timer.Id);
});
<ETTaskCompletionSource>
用于控制计时器任务的生命周期
// 设置此任务的最终状态, 传入true表示超时,传入false表示手动取消
tcs.SetResult(isTimeout);
三种计时器的区别
<OnceWaitTimer>
纯计时
WaitTillAsync\WaitAsync 都使用的这个计时器
<OnceTimer>
直到某个时间调用指定Action
Action<bool> Callback
public long NewOnceTimer(long tillTime, Action action)
public OnceTimer GetOnceTimer(long id)
<RepeatedTimer>
repeatedTime 重复调用的间隔时间
Action<bool> Callback
停止的话可能需要持有对应的id,来调用void ET.TimerComponent.Remove(long id)
public long NewRepeatedTimer(long time, Action<bool> action)
public RepeatedTimer GetRepeatedTimer(long id)
用法示例①:
private async ETVoid TimeoutRemoveKey(long key)
{
await Game.Scene.GetComponent<TimerComponent>().WaitAsync(20000);
this.sessionKey.Remove(key);
}
因为不需要知道任务执行的结果可以使用async ETVoid新开一个协程,
使用计时器组件等待20秒后, 移除对应的SessionKey
用法示例②:
public class ActorLocationSenderComponentAwakeSystem : AwakeSystem<ActorLocationSenderComponent>
{
public override void Awake(ActorLocationSenderComponent self)
{
ActorLocationSenderComponent.Instance = self;
// 每10s扫描一次过期的actorproxy进行回收,过期时间是1分钟
// 可能由于bug或者进程挂掉,导致ActorLocationSender发送的消息没有确认,
// 结果无法自动删除,每一分钟清理一次这种ActorLocationSender
self.CheckTimer = TimerComponent.Instance.NewRepeatedTimer(10 * 1000, self.Check);
}
}
public static void Check(this ActorLocationSenderComponent self, bool isTimeOut)
{
Log.Debug($"{DateTime.Now}每10s扫描一次过期的actorproxy进行回收");
using (ListComponent<long> list = EntityFactory.Create<ListComponent<long>>(self.Domain))
{
long timeNow = TimeHelper.Now();
foreach ((long key, Entity value) in self.Children)
{
ActorLocationSender actorLocationMessageSender = (ActorLocationSender) value;
// ActorLocationSenderComponent.TIMEOUT_TIME 应该设置为60 * 1000
// 可能是猫大手滑打错了, 写成10 * 1000
if (timeNow > actorLocationMessageSender.LastSendOrRecvTime + ActorLocationSenderComponent.TIMEOUT_TIME)
{
list.List.Add(key);
}
}
foreach (long id in list.List)
{
self.Remove(id);
}
}
}
public override void Destroy(ActorLocationSenderComponent self)
{
ActorLocationSenderComponent.Instance = null;
// CheckTimer 是这个重复计时器的ID, 使用Remove方法去取消掉
TimerComponent.Instance.Remove(self.CheckTimer);
self.CheckTimer = 0;
}
创建一个重复执行的计时器, 每10s会调用一次Check方法
(ActorLocationSenderComponentSystem.cs:32) 2020/9/21 20:38:38每10s扫描一次过期的actorproxy进行回收
(ActorLocationSenderComponentSystem.cs:32) 2020/9/21 20:38:48每10s扫描一次过期的actorproxy进行回收
(ActorLocationSenderComponentSystem.cs:32) 2020/9/21 20:38:58每10s扫描一次过期的actorproxy进行回收
(ActorLocationSenderComponentSystem.cs:32) 2020/9/21 20:39:08每10s扫描一次过期的actorproxy进行回收
...
用法示例③ :(暂时还没写完,不过可以看出用法)
// 注释的是5.0里的代码
//public CancellationTokenSource CancellationTokenSource;
//public async ETVoid StartMove(M2C_PathfindingResult message)
//{
// // 取消之前的移动协程
// this.CancellationTokenSource?.Cancel();
// this.CancellationTokenSource = new CancellationTokenSource();
// ...
// await StartMove(this.CancellationTokenSource.Token);
// this.CancellationTokenSource.Dispose();
// this.CancellationTokenSource = null;
//}
----------------------------------------------------------------------------------------
// await self.Parent.GetComponent<MoveComponent>().MoveToAsync(v3, self.CancellationToken);
public async ETTask MoveToAsync(Vector3 target, ETCancellationToken cancellationToken)
{
...
await StartMove(cancellationToken);
}
public async ETTask StartMove(ETCancellationToken cancellationToken)
{
...
TimerComponent timerComponent = Game.Scene.GetComponent<TimerComponent>();
// 协程如果取消,将算出玩家的真实位置,赋值给玩家
cancellationToken.Register(() =>
{
long timeNow = TimeHelper.Now();
if (timeNow - this.StartTime >= this.needTime)
{
unit.Position = this.Target;
}
else
{
float amount = (timeNow - this.StartTime) * 1f / this.needTime;
unit.Position = Vector3.Lerp(this.StartPos, this.Target, amount);
}
});
while (true)
{
await timerComponent.WaitAsync(50, cancellationToken);
long timeNow = TimeHelper.Now();
if (timeNow - this.StartTime >= this.needTime)
{
unit.Position = this.Target;
break;
}
float amount = (timeNow - this.StartTime) * 1f / this.needTime;
unit.Position = Vector3.Lerp(this.StartPos, this.Target, amount);
}
}