C#中如何等待后台计时器驱动的卡片移动进程完成?
解决WinForm卡片移动后的等待逻辑问题
我明白你的问题了——你现在卡在WinForm里卡片移动的时序问题上:卡片移动靠后台计时器的Tick事件驱动,但你需要等卡片完全到位、加入新Deck后再执行后续的卡片比较逻辑,还不能阻塞计时器的运行。之前的while循环无效,核心原因是它把UI线程彻底卡死了——WinForm的计时器Tick事件是在UI线程触发的,线程被循环占住后,计时器根本没机会执行卡片移动的逻辑,AwaitingRecieving自然永远不会变成0。
下面给你两种不用手动管理多线程的简单解决方案:
方法一:用async/await异步等待(推荐)
这种方式不会阻塞UI线程,让计时器能正常处理卡片移动,同时每隔一段时间检查状态直到满足条件:
private async void buCompW1W2_Click(object sender, EventArgs e) { // 启动卡片移动 DeckPOne.MoveCardFromTopToTop(WarPOne); DeckPTwo.MoveCardFromTopToTop(WarPTwo); // 等待卡片完成移动:每隔一个计时器周期检查一次状态 // 把100替换成你的计时器Interval值(比如你的计时器是每50ms Tick一次就写50) while (WarPOne.AwaitingRecieving != 0 || WarPTwo.AwaitingRecieving != 0) { await Task.Delay(100); } // 现在卡片已经到位,执行后续比较逻辑 switch (CompareCards(WarPOne.TopCard, WarPTwo.TopCard)) { case -1: tbRecord.Text += "\n error comparing"; break; case 1: tbRecord.Text += "\n P1 won."; break; case 2: tbRecord.Text += "\n P2 won."; break; case 0: tbRecord.Text += "\n WAR!"; for (int i = 0; i < 3; i++) { DeckPOne.MoveCardFromTopToTop(WarPOne); DeckPTwo.MoveCardFromTopToTop(WarPTwo); } break; } }
原理说明
async/await会让等待过程中UI线程解放出来,继续处理计时器Tick、界面刷新等操作;Task.Delay只是在指定时间后重新执行循环检查,不会阻塞线程。等两个Deck的AwaitingRecieving都变为0,就说明卡片移动完成了。
方法二:利用现有计时器的Tick事件检查状态
如果你不想用异步语法,可以直接复用驱动卡片移动的计时器,在每次Tick时检查是否满足执行后续逻辑的条件:
// 标记是否在等待卡片移动完成 private bool _waitingForCards = false; private void buCompW1W2_Click(object sender, EventArgs e) { // 启动卡片移动 DeckPOne.MoveCardFromTopToTop(WarPOne); DeckPTwo.MoveCardFromTopToTop(WarPTwo); // 设置标记,告诉计时器需要等待卡片到位 _waitingForCards = true; } // 你的计时器Tick事件方法(替换成你实际的计时器方法名) private void cardMovementTimer_Tick(object sender, EventArgs e) { // 保留你原来处理卡片移动的逻辑 // ... 这里是你原来写的卡片位置更新、加入Deck的代码 ... // 检查是否在等待卡片,且卡片已经到位 if (_waitingForCards && WarPOne.AwaitingRecieving == 0 && WarPTwo.AwaitingRecieving == 0) { _waitingForCards = false; // 执行后续比较逻辑 RunCardComparison(); } } private void RunCardComparison() { switch (CompareCards(WarPOne.TopCard, WarPTwo.TopCard)) { case -1: tbRecord.Text += "\n error comparing"; break; case 1: tbRecord.Text += "\n P1 won."; break; case 2: tbRecord.Text += "\n P2 won."; break; case 0: tbRecord.Text += "\n WAR!"; for (int i = 0; i < 3; i++) { DeckPOne.MoveCardFromTopToTop(WarPOne); DeckPTwo.MoveCardFromTopToTop(WarPTwo); } break; } }
原理说明
按钮点击只负责启动移动和设置等待标记,剩下的交给计时器本身。每次Tick时先处理卡片移动,再检查是否满足条件,满足就执行后续逻辑并清除标记,完全不影响计时器的正常运行。
这两种方法都能解决你的问题,而且都不用手动创建和管理额外线程,非常适合WinForm的UI场景。
内容的提问来源于stack exchange,提问作者Petr Klíč
相关产品推荐
相关产品推荐

