停止异步操作以启动替换操作



考虑异步操作,例如从数据存储中获取搜索结果。现在,我希望取消当前挂起的搜索操作,开始一个新的搜索操作成为可能,这实际上取代了活动搜索,主要是因为搜索参数同时发生了变化。

我的搜索是通过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);
}

最新更新