【问题标题】:stop an asynchronous operation to start a replacing one停止异步操作以启动替换操作
【发布时间】:2012-02-28 15:10:43
【问题描述】:

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

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

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

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

那么我是否可能缺少可用于这种场景的框架机制?或者更推荐的方式是什么?

【问题讨论】:

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


    【解决方案1】:

    我相信这就是您正在寻找的。我已经对代码进行了注释,因此大部分推理都嵌入在代码中。

    基本上,让您的 continue 函数处理启动一个准备好的任务。

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

    【讨论】:

    • 是的,充分利用 ContinueWith 是这件作品的关键 :)
    猜你喜欢
    • 2022-07-16
    • 2023-03-16
    • 2016-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-11
    相关资源
    最近更新 更多