остановить асинхронную операцию, чтобы начать заменяющую - PullRequest
1 голос
/ 28 февраля 2012

Рассмотрим асинхронную операцию, например получение результатов поиска из хранилища данных.Теперь я хочу, чтобы можно было отменить текущую операцию поиска, чтобы начать новую, которая фактически заменяет активный поиск, главным образом потому, что параметры поиска тем временем изменились.

Мой поиск начинается через TPL(.NET 4.0) новая задача и в конечном итоге имеет Continues и, конечно, имеет обратные вызовы, которые вызывают UI-методы в UI-Context.

Итак, если кнопка поиска нажимается снова, пока выполняется поиск, яВо-первых, нужно остановить текущее задание и дождаться его завершения.Если я сделаю это из потока пользовательского интерфейса, я могу зайти в тупик, поскольку Wait () в потоке пользовательского интерфейса блокирует его, поэтому возможный Invoke () никогда не будет выполнен.

Поэтому в моем текущем решении я запускаюотдельная задача, которая ожидает запуска / завершения задач, а затем запускает новые.Это кажется немного громоздким и громоздким, и мне интересно, неужели нет более элегантного способа, так как мне часто нужен такой механизм.

Так что, может быть, мне не хватает механизма инфраструктуры, который можно использовать для такого родасценарий?Или что было бы более рекомендуемым способом?

1 Ответ

4 голосов
/ 28 февраля 2012

Я считаю, что это то, что вы ищете. Я прокомментировал код, поэтому большая часть рассуждений встроена в код.

По сути, ваши функции продолжения справляются с раскруткой предварительно подготовленной задачи.

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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...