我有.NET应用程序,GUI上有DataGridView和TextBox。我想做的是,当用户更改TextBox文本时,更新单元格中包含此文本的DataGridView。但这个搜索应该作为异步任务运行,因为如果不是,它会导致GUI冻结。每次用户更改TextBox文本时,如果另一个搜索任务正在运行,我的应用程序都应该取消,并重新运行它以根据新的搜索值进行搜索。这是我的密码;
CancellationTokenSource cts = new CancellationTokenSource();
private async void TextBox1_Changed(object sender, EventArgs e)
{
cts.Cancel();
CancellationToken ct = cts.Token;
try
{
await Task.Run(() =>
{
System.Diagnostics.Debug.WriteLine("Task started");
// Searching here.
System.Diagnostics.Debug.WriteLine("Task finished");
}, cts.Token);
}
catch
{
System.Diagnostics.Debug.WriteLine("Cancelled");
}
}
在我的代码中,任务在没有启动的情况下被取消。我只看到";取消";调试控制台上的行。我应该取消任务,因为如果我不取消,它们的数量和应用程序的CPU使用量就会增加。有办法做到这一点吗?
就像Rand Random所说的,我应该对new CancellationTokenSource
对象进行declear。我已经像这样编辑了我的代码,它很有效。代码应该是这样的:
CancellationTokenSource cts = new CancellationTokenSource();
private async void TextBox1_Changed(object sender, EventArgs e)
{
cts.Cancel();
cts.Dispose();
cts = new CancellationTokenSource();
try
{
await Task.Run(() =>
{
System.Diagnostics.Debug.WriteLine("Task started");
// Searching here.
System.Diagnostics.Debug.WriteLine("Task finished");
}, cts.Token);
}
catch
{
System.Diagnostics.Debug.WriteLine("Cancelled");
}
}
如果你想让自己的生活变得轻松,你应该考虑使用微软的反应式框架(又名Rx(
我假设你可以写这个方法:
async Task<string[]> DoSearchAsync(string text, CancellationToken ct)
{
/* you implement this method */
}
然后你可以这样做:
private IDisposable _searchSubscription = null;
private void Form1_Load(object sender, EventArgs e)
{
_searchSubscription =
Observable
.FromEventPattern(h => TextBox1.TextChanged += h, h => TextBox1.TextChanged -= h)
.Throttle(TimeSpan.FromMilliseconds(400.0))
.Select(ep => TextBox1.Text)
.Select(text => Observable.FromAsync(ct => DoSearchAsync(text, ct)))
.Switch()
.ObserveOn(TextBox1)
.Subscribe(results => { /* update your UI */ });
}
现在,它监视你的TextChanged
事件,等待400.0毫秒,以防你键入另一个字符——如果用户仍在键入,那么启动大量搜索是没有意义的——然后它调用你的新DoSearchAsync
方法。
然后它执行Switch
,这实际上意味着取消任何飞行中的搜索并开始新的搜索。
最后,它整理回UI线程,然后向您提供搜索结果,以便您可以更新UI。
它只处理所有的调用、后台线程调用、编组回UI,所有这些都是为您准备的。
如果您想关闭它,只需调用_searchSubscription.Dispose()
即可。
NuGetSystem.Reactive.Windows.Forms
并加上CCD_。