等待任务在代码触发时不工作,但如果由用户触发,它工作
Await task not working when triggered by code but if by user, it works
我有控制 LED 灯条的应用程序。 UI 具有带效果选择的组合框,当用户选择模式时,它会通过调用 StopTask() 等待当前 运行 效果循环完成,然后执行选定的效果。它通过串行向 Arduino 发送 LED 颜色等。这行得通。
问题是当我通过 MainWindow_OnClosing 触发 StopTask() 时(当用户退出应用程序时),它触发了 StopTask() 但卡在等待 currentEffectMode 上。我将尝试通过代码中的注释来更多地解释它
主窗口模式选择:
private void CbMode_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Checkbox selection triggers this
_ledStrip.LightModes.ChangeMode(CbMode.SelectedIndex);
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
// Trigger disconnect and wait for success - this doesn't work (explained below in code comments)
_ledStrip.LightModes.Disconnect().Wait();
}
灯光模式class:
private Task _modeTask;
private CancellationTokenSource _cancellationToken = new CancellationTokenSource();
// This is being triggered by change mode
internal async void ChangeMode(int mode)
{
// It waits for current loop to finish
await StopTask();
switch (mode)
{
case (int)Modes.Static:
// Then assigns new one
_modeTask = Static(_cancellationToken.Token);
break;
case (int)Modes.Breath:
_modeTask = Breath(_cancellationToken.Token);
break;
}
}
internal async Task StopTask()
{
if (_modeTask == null)
return;
// Set cancellation token to cancel
_cancellationToken.Cancel();
try
{
// and wait for task to finish. This works if triggered by user interaction BUT this is where it gets stuck when called by Disconnect() method (below). It awaits here forever
await _modeTask;
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// After sucessful await create new cts
_cancellationToken.Dispose();
_cancellationToken = new CancellationTokenSource();
}
}
// Example of LED effect loop
internal async Task Static(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
_ledStrip.FillLedsWithColor();
// Wait for strip to light up
await LightLeds();
// Delay before next loop round
await Task.Delay(15, cancellationToken);
}
}
// This is being called by window onclosing
internal async Task Disconnect()
{
//Stop current task and close serial connection. _device is serial
await StopTask();
Application.Current.Dispatcher.Invoke(() =>
{
if (_device.IsOpen())
{
_device.Clear();
_device.Close();
}
});
}
// Method for sending LED information to Arduino
internal async Task LightLeds()
{
if (!_device.IsOpen())
return;
await Task.Run(() =>
{
for (int i = 0; i < StaticValues.NumLeds; i++)
{
_device.Send((byte)i, _ledStrip.Leds[i].LedColor.R, _ledStrip.Leds[i].LedColor.G, _ledStrip.Leds[i].LedColor.B);
}
_device.LightUp();
});
}
我是 Tasks 的初学者,我很确定我没有正确使用它们(其中一些肯定是不必要的,但我不知道),也许这就是它不起作用的原因。我尝试搜索并找到了很多使用任务的示例,但我仍然不太了解它。
谢谢!
将 MainWindow_OnClosing()
改为 async void
,并使用 await Disconnect()
而不是调用 .Wait()
。事件处理程序几乎是唯一可接受的异步方法;其余的应该有一个 async Task[<T>]
签名。 (异步部分有一些例外,但任务部分没有,但我不会在这里混淆水域)。这将阻止您阻止(有关更多信息,请参阅 Dmytro 评论中的 link)。
在那里,将 CbMode_OnSelectionChanged()
更改为相似 (async void
),将 ChangeMode()
设为 async Task
,并改为 await
。
唯一需要注意的其他小事是,如果您将 device-closing 代码移动到您的事件处理程序中(或将其重构为您从事件处理程序调用的另一个方法,在 await Disconnect()
之后),您不需要 Invoke()
,因为异步事件处理程序 - 正确完成 - 免费为您提供;即在不阻塞的情况下有效地保留在 UI 线程上。 (我假设这就是你想要实现的目标?)
我有控制 LED 灯条的应用程序。 UI 具有带效果选择的组合框,当用户选择模式时,它会通过调用 StopTask() 等待当前 运行 效果循环完成,然后执行选定的效果。它通过串行向 Arduino 发送 LED 颜色等。这行得通。
问题是当我通过 MainWindow_OnClosing 触发 StopTask() 时(当用户退出应用程序时),它触发了 StopTask() 但卡在等待 currentEffectMode 上。我将尝试通过代码中的注释来更多地解释它
主窗口模式选择:
private void CbMode_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Checkbox selection triggers this
_ledStrip.LightModes.ChangeMode(CbMode.SelectedIndex);
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
// Trigger disconnect and wait for success - this doesn't work (explained below in code comments)
_ledStrip.LightModes.Disconnect().Wait();
}
灯光模式class:
private Task _modeTask;
private CancellationTokenSource _cancellationToken = new CancellationTokenSource();
// This is being triggered by change mode
internal async void ChangeMode(int mode)
{
// It waits for current loop to finish
await StopTask();
switch (mode)
{
case (int)Modes.Static:
// Then assigns new one
_modeTask = Static(_cancellationToken.Token);
break;
case (int)Modes.Breath:
_modeTask = Breath(_cancellationToken.Token);
break;
}
}
internal async Task StopTask()
{
if (_modeTask == null)
return;
// Set cancellation token to cancel
_cancellationToken.Cancel();
try
{
// and wait for task to finish. This works if triggered by user interaction BUT this is where it gets stuck when called by Disconnect() method (below). It awaits here forever
await _modeTask;
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// After sucessful await create new cts
_cancellationToken.Dispose();
_cancellationToken = new CancellationTokenSource();
}
}
// Example of LED effect loop
internal async Task Static(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
_ledStrip.FillLedsWithColor();
// Wait for strip to light up
await LightLeds();
// Delay before next loop round
await Task.Delay(15, cancellationToken);
}
}
// This is being called by window onclosing
internal async Task Disconnect()
{
//Stop current task and close serial connection. _device is serial
await StopTask();
Application.Current.Dispatcher.Invoke(() =>
{
if (_device.IsOpen())
{
_device.Clear();
_device.Close();
}
});
}
// Method for sending LED information to Arduino
internal async Task LightLeds()
{
if (!_device.IsOpen())
return;
await Task.Run(() =>
{
for (int i = 0; i < StaticValues.NumLeds; i++)
{
_device.Send((byte)i, _ledStrip.Leds[i].LedColor.R, _ledStrip.Leds[i].LedColor.G, _ledStrip.Leds[i].LedColor.B);
}
_device.LightUp();
});
}
我是 Tasks 的初学者,我很确定我没有正确使用它们(其中一些肯定是不必要的,但我不知道),也许这就是它不起作用的原因。我尝试搜索并找到了很多使用任务的示例,但我仍然不太了解它。
谢谢!
将 MainWindow_OnClosing()
改为 async void
,并使用 await Disconnect()
而不是调用 .Wait()
。事件处理程序几乎是唯一可接受的异步方法;其余的应该有一个 async Task[<T>]
签名。 (异步部分有一些例外,但任务部分没有,但我不会在这里混淆水域)。这将阻止您阻止(有关更多信息,请参阅 Dmytro 评论中的 link)。
在那里,将 CbMode_OnSelectionChanged()
更改为相似 (async void
),将 ChangeMode()
设为 async Task
,并改为 await
。
唯一需要注意的其他小事是,如果您将 device-closing 代码移动到您的事件处理程序中(或将其重构为您从事件处理程序调用的另一个方法,在 await Disconnect()
之后),您不需要 Invoke()
,因为异步事件处理程序 - 正确完成 - 免费为您提供;即在不阻塞的情况下有效地保留在 UI 线程上。 (我假设这就是你想要实现的目标?)