线程中止延迟导致显示响应延迟
Thread Abortion Delay causes Delay in Display Response
我正在编写一个程序,该程序使用三个线程更新显示,两个线程都将图像写入显示窗体,一个在需要条件时在两个线程之间切换。我将在底部包含所有三个的代码。我注意到,当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()
是一个 terrible idea,可能会导致各种问题。该示例还看起来像是在后台线程上修改 UI 元素,这也是不允许的,并且可能导致各种问题。
取消某些 运行 后台任务的正确方法是合作。但是,看起来我们根本不需要使用任何后台线程。我们需要的是一个 状态机 和一个定时器。状态机跟踪我们所处的状态、要使用的图像以及当我们在状态之间切换时应该发生什么。计时器用于在一段时间后触发状态更改。
可以为此使用 async/await,因为这将被编译为状态机。而 Task.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
}
}
据我所知,这应该保持相同的行为,而 运行 主线程上的所有代码,并避免线程的所有麻烦。请注意,这需要在 talking/neutral 条件实际更改时通知 class,我的示例使用了一个方法,但它也可以是一个事件。您可能还需要一些方法来停止整个循环,这可以是简单的布尔值或另一个 cancellationTokenSource。
我正在编写一个程序,该程序使用三个线程更新显示,两个线程都将图像写入显示窗体,一个在需要条件时在两个线程之间切换。我将在底部包含所有三个的代码。我注意到,当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()
是一个 terrible idea,可能会导致各种问题。该示例还看起来像是在后台线程上修改 UI 元素,这也是不允许的,并且可能导致各种问题。
取消某些 运行 后台任务的正确方法是合作。但是,看起来我们根本不需要使用任何后台线程。我们需要的是一个 状态机 和一个定时器。状态机跟踪我们所处的状态、要使用的图像以及当我们在状态之间切换时应该发生什么。计时器用于在一段时间后触发状态更改。
可以为此使用 async/await,因为这将被编译为状态机。而 Task.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
}
}
据我所知,这应该保持相同的行为,而 运行 主线程上的所有代码,并避免线程的所有麻烦。请注意,这需要在 talking/neutral 条件实际更改时通知 class,我的示例使用了一个方法,但它也可以是一个事件。您可能还需要一些方法来停止整个循环,这可以是简单的布尔值或另一个 cancellationTokenSource。