c# - 线程中止延迟导致显示响应延迟
问题描述
我正在编写一个程序,它使用三个线程更新显示,两个线程都将图像写入显示表单,一个在需要条件时在两者之间切换。我将在底部包含所有三个的代码。我注意到,当没有音频提示终止talkingThread,以及neutralThread 的创建和启动时,说话的图像会挂在那里几秒钟,持续时间会有所不同。我怀疑它与线程末尾的 Sleep 调用有关,调用 Abort 在清除睡眠之前实际上并没有释放线程,但我不知道这是否会影响另一个线程的重写能力显示器。有什么方法可以确保更快地处理线程?
中性回路 -
private void neutralLoop() //loop for switching to and maintaining the neutral images
{
while (true)
{
if (isBlinking) //if blinking is enabled, check whether or not to blink, and hold for 1 second if so
{
int blinkChance = rnd.Next(2);
if (blinkChance == 1)
{
myImage = this.neutralEyesClosedMap;
pictureBox1.Image = myImage;
Thread.Sleep(1000);
}
}
myImage = this.neutralEyesOpenMap; //replace neutral eyes open state, hold for 5 secs
pictureBox1.Image = myImage;
Thread.Sleep(5000);
}
}
说话循环 -
private void talkingLoop() //loop for switching to and maintaining the talking images
{
while (true)
{
if (isBlinking) //if blinking is enabled, check whether or not to blink, and hold for 1 second if so
{
int blinkChance = rnd.Next(2);
if (blinkChance == 1)
{
myImage = this.talkingEyesClosedMap;
pictureBox1.Image = myImage;
Thread.Sleep(1000);
}
}
myImage = this.talkingEyesOpenMap; //replace neutral eyes open state, hold for 5 secs
pictureBox1.Image = myImage;
Thread.Sleep(5000);
}
}
开关循环
private void switchLoop()
{
neutralThread.Start();
while (true)
{
if (deviceMeter.MasterPeakValue > 0 && deviceMeter.MasterPeakValue < 1) //check for audio
{
if (talkingThread.ThreadState != ThreadState.Running && talkingThread.ThreadState != ThreadState.WaitSleepJoin) //if talkingThread isnt running (neutralThread is running), get rid of it and start talkingThread.
{
neutralThread.Abort();
talkingThread = new Thread(new ThreadStart(talkingLoop));
talkingThread.Start();
}
} else {
if (neutralThread.ThreadState != ThreadState.Running && neutralThread.ThreadState != ThreadState.WaitSleepJoin) //if neutralThread isnt running (talkingThread is running), get rid of it and start neutralThread.
{
talkingThread.Abort();
neutralThread = new Thread(new ThreadStart(neutralLoop));
neutralThread.Start();
}
}
}
}
解决方案
使用Thread.Abort()
是一个糟糕的想法,可能会导致各种问题。该示例也看起来像是在后台线程上修改 UI 元素,这也是不允许的,并且会导致各种问题。
取消一些正在运行的后台任务的正确方法是合作取消。但是,看起来我们根本不需要使用任何后台线程。我们需要的是一个状态机和一个定时器。状态机跟踪我们处于什么状态,使用什么图像,以及当我们在状态之间切换时会发生什么。计时器用于在一段时间后触发状态更改。
可以为此使用 async/await,因为这将被编译为状态机。whileTask.Delay
以可与 async/await 一起使用的方式包装计时器。据我所知,您可以像这样重写您的示例:
private async Task Loop()
{
while (true)
{
cts = new CancellationTokenSource(); // replace the cancellationToken source each iteration
try
{
if (isBlinking) {
int blinkChance = rnd.Next(2);
if (blinkChance == 1)
{
pictureBox1.Image = isneutral ? neutralEyesClosedMap : talkingEyesClosedMap;
await Task.Delay(1000, cts.Token);
}
}
pictureBox1.Image = isneutral ? neutralEyesOpenMap : talkingEyesOpenMap;
await Task.Delay(5000, cts.Token);
}
// Task.Delay may throw when isneutral changes state, this is expected, just continue
catch(OperationCanceledException){}
}
}
public async Task SetMasterPeakValue(double value)
{
var oldValue = isneutral;
isneutral = value > 0 && value < 1;
if (oldValue != isneutral)
{
cts.Cancel(); // cancel the current loop
}
}
据我所知,这应该保持相同的行为,同时在主线程上运行所有代码,并避免线程的所有麻烦。请注意,这需要在谈话/中立条件实际发生变化时通知类,我的示例使用一种方法,但它也可以是一个事件。您可能还需要一些方法来停止整个循环,这可能是简单的布尔值,或者另一个 cancelTokenSource。
推荐阅读
- excel - VBA Chart.SeriesCollection 未按预期工作
- android - android:autofilledHighlight 在左对齐条目上无法正常工作
- php - 使用 laravel 实现 Firebase 在 android 应用中发送通知
- sql - Postgresql 数据透视表和触发器
- webdriver-io - 使用 codeceptJS 在 appium 中单击 Element 不起作用
- html - 如何将 Google Meet 集成到 iframe 中?
- ajax - ajax laravel获取属于select中成绩的值
- azure-logic-apps - 逻辑应用触发器“发生资源事件时”不起作用
- docker - 在 Docker 容器上安装依赖项 - Airflow
- python - 计算字典字典中的增量