如果再次调用异步和 运行 方法并且先前的调用结果已过时,如何正确取消它
How to properly cancel an asynchronous and running method if it is invoked again and the previous invokation result is obsolete
我有一个相当笼统的问题,但有一个特定的用例。
在我的特定情况下,我有一个带有输入文本框的 UI,如果用户正在更改该输入框中的文本,则会显示一些建议,以提示用户最终可能想要输入的内容。换句话说,输入框使用了自动完成功能。这些建议是从网络服务请求的,因此这个过程会花费大量时间,用户可能已经再次更改了文本。因此,我想取消或停止仍在获取过时数据的进程,只获取最近调用该方法的结果。
想象一下视图 class 中的方法 OnTextChanged
正在调用方法 GetRecommendationsAsync
。我的方法是简单地取消以前和过时的 GetRecommendationsAsync
调用,然后如果取消该方法则不在 UI 中执行任何操作。这是我刚刚写的一些最小代码(因此可能包含错误)来展示这个概念:
public async Task OnTextChangedAsync(string newText)
{
try
{
var recommendations = await GetLatestRecommendationsAsync(newText);
ShowRecommendations(recommendations);
}
catch(TaskCanceledException)
{
}
}
private Task<Recommendations[]> GetLatestRecommendationsAsync(string text)
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
if (string.IsNullOrWhiteSpace(text)) return null;
return GetRecommendationsAsync(text, _cancellationTokenSource.Token);
}
现在,我的问题是这是一种有效的方法还是有一些缺点?
此外,我想知道是否有一种普遍的已知模式来处理取消因再次调用而过时的方法的一般情况?
使用信号量的方法更好吗?
你会如何处理这个案例?
是否保证在新调用 returns 其结果之前捕获先前调用的取消?
would like to know if there is a general known pattern to handle the generic case of cancelling a method that is obsolete because it was called again?
取消标记是处理因任何原因而取消的一般已知模式。
Is it guaranteed that the cancellation of the previous invocation is caught before the new invocation returns its results?
没有。这是因为取消令牌用于合作取消,意味着一方请求取消,另一方定期检查取消请求并决定如何取消。这种方法的目的是确保所有需要撤消的事情都被撤消,并且您不会让事情处于不稳定状态。
对比一下,比方说,在后台线程中工作,然后杀死线程以取消。你不知道它是在什么时候被杀死的,也不知道是否有任何东西处于潜在的未知或不稳定状态。
如果出于某种原因,您必须确保甚至 开始 另一次尝试,直到已知前一次尝试已停止,那么您将不得不使用类似信号量的东西(可能连同取消标记)。这完全取决于 GetRecommendationsAsync
在做什么。
旁注:OnTextChangedAsync
的签名看起来很可疑。它的命名就像一个事件,但事件必须 return void
.
我有一个相当笼统的问题,但有一个特定的用例。
在我的特定情况下,我有一个带有输入文本框的 UI,如果用户正在更改该输入框中的文本,则会显示一些建议,以提示用户最终可能想要输入的内容。换句话说,输入框使用了自动完成功能。这些建议是从网络服务请求的,因此这个过程会花费大量时间,用户可能已经再次更改了文本。因此,我想取消或停止仍在获取过时数据的进程,只获取最近调用该方法的结果。
想象一下视图 class 中的方法 OnTextChanged
正在调用方法 GetRecommendationsAsync
。我的方法是简单地取消以前和过时的 GetRecommendationsAsync
调用,然后如果取消该方法则不在 UI 中执行任何操作。这是我刚刚写的一些最小代码(因此可能包含错误)来展示这个概念:
public async Task OnTextChangedAsync(string newText)
{
try
{
var recommendations = await GetLatestRecommendationsAsync(newText);
ShowRecommendations(recommendations);
}
catch(TaskCanceledException)
{
}
}
private Task<Recommendations[]> GetLatestRecommendationsAsync(string text)
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
if (string.IsNullOrWhiteSpace(text)) return null;
return GetRecommendationsAsync(text, _cancellationTokenSource.Token);
}
现在,我的问题是这是一种有效的方法还是有一些缺点? 此外,我想知道是否有一种普遍的已知模式来处理取消因再次调用而过时的方法的一般情况? 使用信号量的方法更好吗? 你会如何处理这个案例? 是否保证在新调用 returns 其结果之前捕获先前调用的取消?
would like to know if there is a general known pattern to handle the generic case of cancelling a method that is obsolete because it was called again?
取消标记是处理因任何原因而取消的一般已知模式。
Is it guaranteed that the cancellation of the previous invocation is caught before the new invocation returns its results?
没有。这是因为取消令牌用于合作取消,意味着一方请求取消,另一方定期检查取消请求并决定如何取消。这种方法的目的是确保所有需要撤消的事情都被撤消,并且您不会让事情处于不稳定状态。
对比一下,比方说,在后台线程中工作,然后杀死线程以取消。你不知道它是在什么时候被杀死的,也不知道是否有任何东西处于潜在的未知或不稳定状态。
如果出于某种原因,您必须确保甚至 开始 另一次尝试,直到已知前一次尝试已停止,那么您将不得不使用类似信号量的东西(可能连同取消标记)。这完全取决于 GetRecommendationsAsync
在做什么。
旁注:OnTextChangedAsync
的签名看起来很可疑。它的命名就像一个事件,但事件必须 return void
.