在.netcore6中使用keydown方法对键盘进行监听,但是要是一直按着某个按键就会阻塞我的主线程
public async void KeyDownAction(object? sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space || e.KeyCode == Keys.W || e.KeyCode == Keys.D || e.KeyCode == Keys.A || e.KeyCode == Keys.S)
{
switch (e.KeyCode)
{
case Keys.Space:
this.timer1.Stop(); break;
case Keys.W:
if (this._snake.direction == Direction.DOWN) break;
this._snake.direction = Direction.UP; break;
case Keys.S:
if (this._snake.direction == Direction.UP) break; this._snake.direction = Direction.DOWN; break;
case Keys.A: if (this._snake.direction == Direction.RIGHT) break; this._snake.direction = Direction.LEFT; break;
case Keys.D: if (this._snake.direction == Direction.LEFT) break; this._snake.direction = Direction.RIGHT; break;
}
}
}
这段代码旨在改变蛇的行进方向
在这段代码中,KeyDown事件的处理器中有一个问题,那就是如果一直按住某个键,就会频繁触发KeyDown事件,导致主线程被阻塞。
要解决这个问题,可以采取以下措施:
一、使用一个Timer在KeyDown事件后延迟一段时间再执行逻辑,比如:
Timer timer = null;
void KeyDownAction(object sender, KeyEventArgs e)
{
if (timer != null) timer.Dispose();
timer = new Timer(Delay);
timer.Elapsed += (s, ea) =>
{
// 键盘逻辑处理
...
};
}
private int Delay = 200; // 延迟时间ms
这样可以避免在按键期间过快触发逻辑处理。
二、使用异步方法并await Task.Delay延迟处理:
async void KeyDownAction(object sender, KeyEventArgs e)
{
await Task.Delay(200); // 延迟200ms
// 键盘逻辑处理
...
}
三、使用System.Threading.Timer启动一个后台线程定期检查键盘状态,这样主线程就不会被阻塞:
Timer timer = null;
void KeyDownAction(object sender, KeyEventArgs e)
{
timer = new Timer(Checkkeyboard);
timer.Change(0, 50); // 每50ms检查一次
}
private void CheckKeyboard(object state)
{
if (Keyboard.IsKeyDown(Key.Space)) { /* Do Something */ }
}
四、使用Observable.Interval在Rx中周期性检查按键状态:
var keyObservable = Observable.Interval(TimeSpan.FromMilliseconds(50))
.Subscribe(x => {
if (Keyboard.IsKeyDown(Key.Space)) { /* Do Something */ }
});
以上就是几种解决方案,总的来说就是采用定时器或线程来周期检查键盘状态,而不是直接在KeyDown事件中执行所有的逻辑,这可以避免主线程被大量的键盘事件阻塞。
尝试一下这些方案,或许能够帮你解决问题。
建议每个按键的逻辑分开写,不要写在一起
简单处理 就是加个lock锁,代码上看也没有线程异步,就是同步方法。所以加个lock锁让他们排队即可解决
可以添加时间间隔,降低调用的频率来提高程序的响应效率。可以设定连着按时每秒触发10次,10此以内忽略掉。参考如下代码:
private void KeyDownAction(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space || e.KeyCode == Keys.W || e.KeyCode == Keys.D || e.KeyCode == Keys.A || e.KeyCode == Keys.S)
{
if (DateTime.Now.Ticks - last < 1000000)//100ms以内忽略
return;
last = DateTime.Now.Ticks;
switch (e.KeyCode)
{
case Keys.Space:
this.timer1.Stop(); break;
case Keys.W:
if (this._snake.direction == Direction.DOWN) break;
this._snake.direction = Direction.UP; break;
case Keys.S:
if (this._snake.direction == Direction.UP) break; this._snake.direction = Direction.DOWN; break;
case Keys.A: if (this._snake.direction == Direction.RIGHT) break; this._snake.direction = Direction.LEFT; break;
case Keys.D: if (this._snake.direction == Direction.LEFT) break; this._snake.direction = Direction.RIGHT; break;
}
}
}
在窗体内添加一个long变量记录上次运行的tick。
private long last;
将此textView的宽高设置成固定大小,这样只会更新文字
试试使用Task.Run
方法将处理逻辑放到后台线程执行,避免阻塞主线程。
public async void KeyDownAction(object? sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space || e.KeyCode == Keys.W || e.KeyCode == Keys.D || e.KeyCode == Keys.A || e.KeyCode == Keys.S)
{
await Task.Run(() =>
{
switch (e.KeyCode)
{
case Keys.Space:
this.timer1.Stop(); break;
case Keys.W:
if (this._snake.direction == Direction.DOWN) break;
this._snake.direction = Direction.UP; break;
case Keys.S:
if (this._snake.direction == Direction.UP) break; this._snake.direction = Direction.DOWN; break;
case Keys.A: if (this._snake.direction == Direction.RIGHT) break; this._snake.direction = Direction.LEFT; break;
case Keys.D: if (this._snake.direction == Direction.LEFT) break; this._snake.direction = Direction.RIGHT; break;
}
});
}
}
参考gpt:
在你的代码中,你使用了async void来定义KeyDownAction方法,但是在事件处理程序中使用async void是不推荐的做法,因为它可能导致无法处理异常或取消操作。此外,你也提到在一直按住某个按键时会阻塞主线程,这是因为KeyDown事件在按键按下时触发,而不是按住期间持续触发。
如果你希望在按键按下期间持续触发事件,可以使用KeyUp事件来监听键盘释放的动作,然后根据按键状态来判断蛇的行进方向。下面是一个使用KeyUp事件的示例代码:
private Dictionary<Keys, bool> keyState = new Dictionary<Keys, bool>();
public MainForm()
{
InitializeComponent();
this.KeyPreview = true;
this.KeyUp += KeyUpAction;
this.KeyDown += KeyDownAction;
}
private void KeyDownAction(object? sender, KeyEventArgs e)
{
keyState[e.KeyCode] = true;
HandleKeyState();
}
private void KeyUpAction(object? sender, KeyEventArgs e)
{
keyState[e.KeyCode] = false;
HandleKeyState();
}
private void HandleKeyState()
{
if (keyState.ContainsKey(Keys.Space) && keyState[Keys.Space])
{
this.timer1.Stop();
}
else if (keyState.ContainsKey(Keys.W) && keyState[Keys.W])
{
if (this._snake.direction != Direction.DOWN)
this._snake.direction = Direction.UP;
}
else if (keyState.ContainsKey(Keys.S) && keyState[Keys.S])
{
if (this._snake.direction != Direction.UP)
this._snake.direction = Direction.DOWN;
}
else if (keyState.ContainsKey(Keys.A) && keyState[Keys.A])
{
if (this._snake.direction != Direction.RIGHT)
this._snake.direction = Direction.LEFT;
}
else if (keyState.ContainsKey(Keys.D) && keyState[Keys.D])
{
if (this._snake.direction != Direction.LEFT)
this._snake.direction = Direction.RIGHT;
}
}
在这个示例中,我们使用keyState字典来保存按键的状态,true表示按键按下,false表示按键释放。当键盘的按键状态发生改变时,调用HandleKeyState方法来根据按键状态来处理蛇的行进方向。
通过这种方式,即使用户按住某个按键,也可以在HandleKeyState方法中根据按键状态连续更新蛇的行进方向,而不会阻塞主线程。