在取消和处置CancellationTokenSource之间有延迟是否提供IsCancellationRequeste



c# Windows UWP项目

我在调用另一个方法的异步方法中实现了CancellationTokenSource和CancellationToken。此方法包含一个while循环,该循环将bool变量的值保持为true,直到令牌源被取消。

async方法由鼠标左键按下事件触发,并由使用ColorPicker控件时发生的鼠标左键释放事件取消。bool变量允许在true时将颜色值发送给设备,在false时阻止它。

通过保持该值为true,当鼠标在颜色选择器周围移动时,只要鼠标按钮保持向下,设备就会连续接收不同的颜色值。一旦鼠标按钮被释放,产生的假值(由发送颜色值到设备的例程设置)将阻止进一步的颜色消息被发送到设备。

我的代码也做了我想要的,但我担心如果我没有正确实现它可能会产生潜在的副作用。我在这个论坛上至少看到过一个帖子,表明顺序:取消,处置和设置为null可以用于CancellationTokenSource。但让我担心的是,我有一个可能无限的while循环,它完全依赖于接收取消令牌。所以我的问题是过早处理CancellationTokenSource是否会阻止令牌。iscanellationrequest被设置为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"下面的will提供了while循环终止的保证吗?

。取消模型是协作的,调用Cancel只是通过将所有令牌的IsCancellationRequested属性设置为true来提供取消的通知

然后由任何可取消的API,即任何接受CancellationToken的方法来监视该属性的值并响应取消请求。

所以ColorPicker_PtrReleasedEvent不能保证while循环在AllowColorChange结束。

根据TZ的建议,我修改了最后三个方法来等待AllowColorChange(token)方法的取消标志,然后将该结果用作处理()CancellationTokenResult并将其设置为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;
}
}
}
}
}

}

最新更新