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

时间:2012-02-28 15:10:43

标签: c#-4.0 asynchronous task-parallel-library

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

我的搜索是通过TPL(.NET 4.0)新任务开始的,最终有Continues,当然还有回调,在UI-Context上调用UI-Methods。

因此,如果再次按下搜索按钮,当搜索尝试运行时,我首先必须停止当前任务并等待它结束。如果我从UI线程那样做,我可能会遇到死锁,因为UI线程上的Wait()会阻塞它,所以可能永远不会执行Invoke()。

因此,在我当前的解决方案中,我启动一个单独的任务,等待运行任务结束/中止然后运行新任务。这感觉有点笨重和笨重,我想,如果没有更优雅的方式,因为我经常需要这种机制。

所以我可能错过了一种可用于这种情况的框架机制?或者更推荐的方式是什么?

1 个答案:

答案 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);
}