考虑异步操作,例如从数据存储中获取搜索结果。现在,我希望取消当前挂起的搜索操作,开始一个新的搜索操作成为可能,这实际上取代了活动搜索,主要是因为搜索参数同时发生了变化。
我的搜索是通过TPL(.NET 4.0)新任务开始的,最终有Continues,当然还有回调,调用UI上下文上的UI方法。
所以,如果再次按下搜索按钮,而搜索品尝正在运行,我首先必须停止当前任务,等待它结束。如果我在UI线程中这样做,我可能会遇到死锁,因为UI线程上的Wait()会阻止它,所以可能的Invoke()永远不会执行。
因此,在我当前的解决方案中,我启动一个单独的任务,等待正在运行的任务结束/中止,然后运行新的任务。这感觉有点笨重,我想知道,如果没有更优雅的方式,因为我经常需要这种机制。
那么,我可能错过了一个可用于这种场景的框架机制吗?或者更推荐的方式是什么?
我相信这就是您想要的。我已经对代码进行了注释,所以大部分推理都嵌入了代码中。
基本上,让你的continue函数处理一个准备好的Task。
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);
}