取消线程并重新启动它

当用户调整窗口大小时,应该更新一些长文本,但是如果线程已在运行,则应该停止并使用新的width参数重新启动它。

int myWidth; private CancellationTokenSource tokenSource2 = new CancellationTokenSource(); private CancellationToken ct = new CancellationToken(); void container_Loaded(object sender, RoutedEventArgs e) { ct = tokenSource2.Token; MyFunction(); } void container_SizeChanged(object sender, SizeChangedEventArgs e) { if (tokenSource2.Token.IsCancellationRequested) MyFunction(); else tokenSource2.Cancel(); } void MyFunction() { myWidth = GetWidth(); Task.Factory.StartNew(() => { string s; for (int i=0;i { ShowText(s); })); },tokenSource2.Token) .ContinueWith(t => { if (t.IsCanceled) { tokenSource2 = new CancellationTokenSource(); //reset token MyFunction(); //restart }; }); } 

现在发生的事情是,当我调整窗口大小时,我会看到文本在接下来的几秒内迭代地更新,好像旧线程没有被取消一样。 我究竟做错了什么?

在这种情况下,我认为使用全局变量不是一个好主意。 这是我如何使用我的AsyncOp从相关问题的答案中做到这一点, 正确地取消异步操作并再次触发它 :

 using System; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace Wpf_21611292 { // Model public class ViewModel : INotifyPropertyChanged { string _data; public string Data { get { return _data; } set { if (_data != value) { _data = value; if (this.PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Data")); } } } public event PropertyChangedEventHandler PropertyChanged; } // MainWindow public partial class MainWindow : Window { ViewModel _model = new ViewModel { Data = "Hello!" }; AsyncOp _asyncOp = new AsyncOp(); CancellationTokenSource _myFunctionCts = new CancellationTokenSource(); public MainWindow() { InitializeComponent(); this.DataContext = _model; this.Loaded += MainWindow_Loaded; this.SizeChanged += MainWindow_SizeChanged; } void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e) { _asyncOp.RunAsync(MyFunctionAsync, _myFunctionCts.Token); } void MainWindow_Loaded(object sender, RoutedEventArgs e) { _asyncOp.RunAsync(MyFunctionAsync, _myFunctionCts.Token); } async Task MyFunctionAsync(CancellationToken token) { int width = (int)this.Width; var text = await Task.Run(() => { int i; for (i = 0; i < 10000000; i++) { if (token.IsCancellationRequested) break; } return i; }, token); // update ViewModel _model.Data = "Width: " + width.ToString() + "/" + text; } } // AsyncOp class AsyncOp { Task _pendingTask = null; CancellationTokenSource _pendingCts = null; public Task PendingTask { get { return _pendingTask; } } public void Cancel() { if (_pendingTask != null && !_pendingTask.IsCompleted) _pendingCts.Cancel(); } public Task RunAsync(Func routine, CancellationToken token) { var oldTask = _pendingTask; var oldCts = _pendingCts; var thisCts = CancellationTokenSource.CreateLinkedTokenSource(token); Func startAsync = async () => { // await the old task if (oldTask != null && !oldTask.IsCompleted) { oldCts.Cancel(); try { await oldTask; } catch (Exception ex) { while (ex is AggregateException) ex = ex.InnerException; if (!(ex is OperationCanceledException)) throw; } } // run and await this task await routine(thisCts.Token); }; _pendingCts = thisCts; _pendingTask = Task.Factory.StartNew( startAsync, _pendingCts.Token, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).Unwrap(); return _pendingTask; } } } 

XAML:

              

它使用async/await ,因此如果您以.NET 4.0为目标,则需要Microsoft.Bcl.Async和VS2012 +。 或者,您可以将async/await转换为ContinueWith ,这有点单调乏味,但总是可行的(这或多或少是C#5.0编译器在场景后的作用)。