考虑异步操作,例如从数据存储中获取搜索结果。现在我想要取消当前待处理的搜索操作,开始一个新的,实际上取代了主动搜索,主要是因为搜索参数同时发生了变化。
我的搜索是通过TPL(.NET 4.0)新任务开始的,最终有Continues,当然还有回调,在UI-Context上调用UI-Methods。
因此,如果再次按下搜索按钮,当搜索尝试运行时,我首先必须停止当前任务并等待它结束。如果我从UI线程那样做,我可能会遇到死锁,因为UI线程上的Wait()会阻塞它,所以可能永远不会执行Invoke()。
因此,在我当前的解决方案中,我启动一个单独的任务,等待运行任务结束/中止然后运行新任务。这感觉有点笨重和笨重,我想,如果没有更优雅的方式,因为我经常需要这种机制。
所以我可能错过了一种可用于这种情况的框架机制?或者更推荐的方式是什么?
答案 0 :(得分:4)
我相信这就是你要找的东西。我已经对代码进行了评论,因此大多数推理都嵌入在代码中。
基本上,让你的继续功能处理启动准备好的任务。
Task currentTask;
CancellationTokenSource cancelTokenSource;
CancellationToken cancelToken;
Task newTask;
CancellationTokenSource newCancelTokenSource;
CancellationToken newCancelToken;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if(currentTask != null && currentTask.Status == TaskStatus.Running)
{
//Cancel the running task
cancelTokenSource.Cancel();
//Prepare a new Task to be triggered when the other cancels
//You could store new tasks/tokens in a dictionary if you wanted,also
//A new cancel token is always needed since the old stays cancelled
newCancelTokenSource = new CancellationTokenSource();
newCancelToken = newCancelTokenSource.Token;
newTask = new Task(()=>LongRunningTask(), newCancelToken);
//Continue that deals with both cancel and completion
//There is a different way to deal with this below, also
newTask.ContinueWith((previousTask)=>
{
if(previousTask.Status == TaskStatus.Cancelled)
{
label1.Text = "New Task Cancelled, Another New Starting";
BeginNewTask();
}
else
label1.Text = "New Task Ran To Completion";
},
//If cancelled token is passed, it will autoskip the continue
new CancellationTokenSource().Token, TaskContinuationOptions.None,
//This is to auto invoke the UI thread
TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
cancelTokenSource = new CancellationTokenSource();
cancelToken = cancelTokenSource.Token;
//Start a fresh task since none running
currentTask = Task.Factory.StartNew(()=>LongRunningTask(),
cancelToken);
//OnCancelContinue
currentTask.ContinueWith((previousTask)=>
{
label1.Text = "First Task Cancelled, New Starting";
BeginNewTask();
},
//If cancelled token is passed, it will autoskip the continue
new CancellationTokenSource().Token,
TaskContinuationOptions.OnlyOnCancelled,
//This is to auto invoke the UI thread
TaskScheduler.FromCurrentSynchronizationContext());
//OnCompleteContinue
currentTask.ContinueWith((previousTask)=>
{
label1.Text = "First Task Ran To Completion";
},
//If cancelled token is passed, it will autoskip the continue
new CancellationTokenSource().Token,
TaskContinuationOptions.OnlyOnRanToCompletion,
//This is to auto invoke the UI thread
TaskScheduler.FromCurrentSynchronizationContext());
}
}
private void LongRunningTask()
{
for(int i = 0; i < 60; i++)
{
if(cancelToken.IsCancellationRequested)
cancelToken.ThrowIfCancellationRequested();
Thread.Sleep(1000);
}
}
private void BeginNewTask()
{
//Since the old task is cancelled, reset it with the new one
//Probably should do some error checks
currentTask = newTask;
cancelTokenSource = newCancelTokenSource;
cancelToken = newCancelToken;
//This is to make sure this task does not run on the UI thread
currentTask.Start(TaskScheduler.Default);
}