如何解决取消和处理 CancellationTokenSource 之间的延迟是否可以保证 IsCancellationRequested 将设置为 true?
C# Windows UWP 项目
我已经在调用另一个方法的异步方法中实现了 CancellationTokenSource 和 CancellationToken。此方法包含一个 while 循环,该循环将 bool 变量的值保持为 true,直到令牌源被取消。
异步方法由鼠标左键按下事件触发,并由使用 ColorPicker 控件时发生的鼠标左键释放事件取消。 bool 变量允许在为真时将颜色值发送到设备,在为假时阻止它。
通过将该值保持为 true,只要鼠标按钮保持按下,设备就会在指针在颜色选择器周围移动时不断接收变化的颜色值。释放鼠标按钮后,产生的假值(由将颜色值发送到设备的例程设置)阻止进一步的颜色消息发送到设备。
我的代码也能满足我的要求,但我担心如果我没有正确实现它可能会产生潜在的副作用。我在这个论坛上至少看到一个帖子,表明序列:cancel、dispose 和 set to null 可用于 CancellationTokenSource。但让我担心的是,我有一个潜在的无限 while 循环,它完全取决于接收取消令牌。所以我的问题是过早处理 CancellationTokenSource 是否会阻止 token.IsCanellationRequested 被设置为 true,如果是这样,添加延迟会增加任何好处吗?
以下是我的代码中的相关片段:
全局变量:
public static bool colorPickerPtrpressed = false;
static CancellationTokenSource cts = null;
static CancellationToken token;
鼠标按钮事件:
private void ColorPicker_PtrpressedEvent(object sender,PointerRoutedEventArgs e)
{
if(cts == null) cts = new CancellationTokenSource();
token = cts.Token;
var picker = sender as ColorPicker.ColorPicker;
colorPickerPtrpressed = true;
(picker.DataContext as Spheroviewmodel).Color = picker.SelectedColor.Color;
ColorChange(token);
}
private void ColorPicker_PtrReleasedEvent(object sender,PointerRoutedEventArgs e)
{
if (cts != null)
{
cts.Cancel();
Task.Delay(500).Wait(); // Allow some time for cancel to take effect
cts.dispose();
cts = null;
}
}
取消令牌方法:
public static async Task ColorChange(CancellationToken token)
{
await Task.Run(() =>
AllowColorChange(token),token);
}
public static void AllowColorChange(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
colorPickerPtrpressed = true; // Maintain value as true
Task.Delay(100).Wait(); // allow color updates periodically
}
return;
}
解决方法
所以我的问题是,在取消和处理 CancellationTokenSource 之间是否有延迟,正如我在下面的“ColorPicker_PtrReleasedEvent”中所做的那样,是否可以保证 while 循环将终止?
没有。取消模型是合作的,调用 Cancel
只是通过将所有令牌的 IsCancellationRequested
属性设置为 true
来提供取消的通知。
然后由任何可取消的 API(即接受 CancellationToken
的任何方法)来监视此属性的值并响应取消请求。
所以 ColorPicker_PtrReleasedEvent
不能保证 while
循环会在 AllowColorChange
处终止。
按照 TZ 的建议,我修改了最后三个方法以等待来自 AllowColorChange(token) 方法的取消标志,然后使用该结果作为对 CancellationTokenResult 的 dispose() 的许可,并将其设置为 null。如果有人发现我所做的有问题,请告诉我。以下是修改后的代码,看起来运行良好:
private void ColorPicker_PntrReleasedEvent(object sender,PointerRoutedEventArgs e)
{
if (cts != null)
{
cts.Cancel();
//Task.Delay(200).Wait(); // Allow some time for cancel to take effect
//cts.Dispose();
//cts = null;
}
}
public static async Task ColorChange(CancellationToken token)
{
bool task = false;
task = await Task.Run<bool>(() =>
AllowColorChange(token),token);
if (task)
{
cts.Dispose();
cts = null;
}
else
{
// Shouldn't ever reach this point
bool isBrkPnt = true;
}
}
public static async Task<bool> AllowColorChange(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
colorPickerPtrPressed = true; // Maintain value as true
await Task.Delay(100); // allow color updates periodically
}
return true; // signal that task was canceled
}
}
,
在实施 Theodor Zoulias 提出的建议后,最终代码如下所示。可以看出,在取消和处理 CancellationTokenSource 之间没有使用任意延迟,而是将处理移动到 catch{} 块,该块由抛出 token.ThrowIfCancellationRequested(); 导致的 OperationCanceledException 触发;在 while() 循环中,它被移动到 try{} 块中,并且它的测试参数设置为 true。不再需要测试 token.IsCancellationRequested 的值作为 while 循环的参数。此编码可确保在取消 try{} 块内的任务之前不会发生处置。
private void ColorPicker_PntrPressedEvent(object sender,PointerRoutedEventArgs e)
{
if(cts == null) cts = new CancellationTokenSource();
token = cts.Token;
var picker = sender as ColorPicker.ColorPicker;
colorPickerPtrPressed = true; // True allows new values of color to be sent to device
(picker.DataContext as SpheroViewModel).Color = picker.SelectedColor.Color;
ColorChange(token); // Don't await this
}
private void ColorPicker_PntrReleasedEvent(object sender,PointerRoutedEventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}
public static async Task ColorChange(CancellationToken token)
{
try
{
await Task.Run(async () =>
{
while (true)
{
token.ThrowIfCancellationRequested();
colorPickerPtrPressed = true; // Maintain value as true while mouse button remains pressed
await Task.Delay(100,token); // allow color updates periodically
}
},token);
}
catch (OperationCanceledException ex) when (ex.CancellationToken == token) // includes TaskCanceledException
{
if (cts != null) // Shouldn't arrive here if it is null but check just in case
{
try
{
cts.Dispose();
cts = null;
}
catch (ObjectDisposedException e)
{
// Already disposed,do nothing
bool brkPnt = true;
}
}
}
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。