发现了一个关于SendEvent和Task之间可能存在的冲突bug。
一句话总结:await client.SendEvent()
调用后面必须紧跟一个print语句,否则则会导致yield return new WaitUntil(() => task.IsCompleted);
的task一直是未完成的状态,卡在这里无法继续执行。
补充信息:
如果打开消息日志,bug就会消失(task会完成),如果注释掉下面两行消息日志的代码,bug就会出现。
LeanCloud.Common.Logger.LogDelegate = (level, info) =>
{ Debug.Log($"[{level}] {info}"); };
详细情况:
主要用了球球大作战demo的机制(master用Coroutine每秒执行一次倒计时),在某些时间点会执行一些计算并SetProperties或SendEvents,由于这些是异步任务所以使用了WaitUntil(() => task.IsCompleted)
的方式中断Coroutine。
代码如下:
IEnumerator CountingCoroutine()
{
while (true)
{
var client = LeanCloudUtils.GetClient();
var props = client.Room.CustomProperties;
if (props.GetByte(Keys.STATE) == Constants.STATE_PLAY)
{
var countDown = props.GetInt(Keys.COUNTDOWN);
countDown -= 1;
if (countDown <= 0)
{
var task = ChangeTurn();
print("================= waiting ==================");
yield return new WaitUntil(() => task.IsCompleted);
print("================= completed ==================");
}
else
{
var newProps = new PlayObject {
{ Keys.COUNTDOWN, countDown }
};
client.Room.SetCustomProperties(newProps);
}
}
yield return new WaitForSeconds(Constants.COUNTING_INTERVAL);
}
}
async Task ChangeTurn()
{
print("更换回合");
var client = LeanCloudUtils.GetClient();
var props = client.Room.CustomProperties;
var oldActorId = props.GetInt(Keys.WHOSE_TURN);
// 旧用户停止操作 问题出在以下print的1、2之间
print("============== 1 ==============="); // print 1
var oldPlayer = client.Room.GetPlayer(oldActorId);
await client.SendEvent(Constants.EVENT_CONTROL_END, options: new SendEventOptions
{
TargetActorIds = new List<int> { oldPlayer.ActorId }
});
print("============== 2 ==============="); // print 2
// 计算得到下一个回合的actorID
var nextPlayer = client.Room.PlayerList[0];
if (oldActorId == nextPlayer.ActorId) nextPlayer = client.Room.PlayerList[1];
var nextActorId = nextPlayer.ActorId;
var turn = props.GetInt(Keys.TURN);
turn += 1;
var newProps = new PlayObject {
{ Keys.WHOSE_TURN, nextActorId },
{ Keys.TURN, turn },
{ Keys.COUNTDOWN, 4 }
};
await client.Room.SetCustomProperties(newProps);
// 抽卡
await DrawForPlayer(nextPlayer); // 这里的调用有SetCustomProperties、也有SendEvent
}
问题出在print 1、2之间的那次SendEvent。如果将SendEvent部分(1、2之间所有代码)都注释掉,可以正常运行;如果带有两行print语句及中间的代码,也可以正常运行。但如果只保留SendEvent部分但不带两行Print语句,则会导致卡住,即WaitUntil(() => task.IsCompleted)
判断一直是失败的。经过测试,“print 2”行缺少就会导致卡住。实际项目中后面还会有一个sendEvent,经过各种注释掉的测试发现好像sendEvent后不加一个print,就会导致卡住。