Как отменить заблокированную задачу в C# с помощью токена отмены?

У меня есть задача, которая всегда заблокирована, и в нее передан CancellationToken, который используется для отмены задачи. Однако никогда не выполняется задача продолжения, которая должна выполняться при отмене задачи. Код:

    _tokenSrc = new CancellationTokenSource();
    var cnlToken = _tokenSrc.Token;

    Task.Run(() => 
          // _stream.StartStream() blocks forever  
          _stream.StartStream(), cnlToken)
        .ContinueWith(ant =>
        {
            _logger.Warn("Stream task cancellation requested, stopping the stream");
            _stream.StopStream();
            _stream = null;
            _logger.Warn("Stream stopped and task cancelled");
        }, TaskContinuationOptions.OnlyOnCanceled);

Позже где-то еще в коде...

_tokenSrc.Cancel();

Причина, по которой мне пришлось использовать задачу для _stream.StartStream(), заключается в том, что этот вызов блокируется навсегда (API, над которым я не контролирую, обратите внимание, что _stream относится к стороннему API, который передает данные из веб-службы), поэтому мне пришлось вызвать это в другой ветке.

Как лучше всего отменить задание?


person MaYaN    schedule 29.03.2014    source источник
comment
Не уверен, что вы пытаетесь здесь сделать... _tokenSrc.Cancel(); - правильный способ отменить задачу. Однако вы также должны отслеживать этот токен на предмет отмены внутри своей задачи. Это не произойдет само собой. Вы делаете страшные вещи здесь, используя глобальные потоки. В этом нет смысла, потому что ваша задача-продолжение может происходить внутри основной задачи. Суть в том, что вы пытаетесь сделать? Зачем вам задачи для этого? Пожалуйста, ответьте на эти два вопроса, отредактировав свой вопрос выше.   -  person Neolisk    schedule 29.03.2014
comment
Этот поток представляет собой API, который я использую и который был разработан ужасным образом, над которым я не властен. поскольку вызов _stream.StartStream() никогда не возвращается, у меня нет возможности проверить статус cnlToken, скажем, в цикле, как бы вы это реализовали?   -  person MaYaN    schedule 29.03.2014
comment
@MaYaN: вы не можете использовать отмену TPL, если не можете проверить отмену. Маркер отмены — это только средство для реализации отмены задачи, но не ее принудительное выполнение. Может быть, есть API для отмены потока из другого потока?   -  person    schedule 29.03.2014
comment
@jdv-JandeVaan: Спасибо за ответ, я начинаю приходить к тому же выводу :-) Мне нужно будет завершить задачу как очистку. Вероятно, я мог бы запустить дочернюю задачу, которая запускает поток, а затем периодически проверяет наличие IsCancellationRequested в родительской задаче.   -  person MaYaN    schedule 29.03.2014
comment
@MaYaN: Что сказал Ян де Ваан. Отмена должна быть совместной, принудительного исполнения нет. Посмотрите на это как на обходной путь: stackoverflow .com/questions/4740856/ и этот social.msdn.microsoft.com/Forums/vstudio/en-US/   -  person Neolisk    schedule 29.03.2014
comment
@Neolisk: Tnx, это был мой запасной план, которого я планировал избежать :-)   -  person MaYaN    schedule 29.03.2014


Ответы (3)


[ОБНОВИТЬ]

Я изменил код ниже, который устранил проблему:

Task.Run(() =>
{
    var innerTask = Task.Run(() => _stream.StartStream(), cToken);
    innerTask.Wait(cToken);
}, cToken)
.ContinueWith(ant =>
{
    _logger.Warn("Stream task cancellation requested, stopping the stream");
    _stream.StopStream();
    _stream = null;
    _logger.Warn("Stream stopped and task cancelled");
}, TaskContinuationOptions.OnlyOnCanceled);
person MaYaN    schedule 29.03.2014

Вы можете использовать метод Register для CancellationToken для регистрации делегата, который будет вызываться при запросе на отмену. В делегате вы вызываете метод, который разблокирует вашу заблокированную операцию.

Что-то типа:

_tokenSrc = new CancellationTokenSource();
var cnlToken = _tokenSrc.Token;

var task = Task.Factory.StartNew(() =>
{
    using(var tokenReg = cnlToken.Register(() => 
        {
            _logger.Warn("Stream task cancellation requested, stopping the stream");
            _stream.StopStream();
            _stream = null;
            _logger.Warn("Stream stopped and task cancelled");
        }))
    {
        _stream.StartStream(), cnlToken)
    }
}, cnlToken);

MSDN "Как зарегистрировать обратные вызовы для запросов на отмену"

person Owen Pauling    schedule 08.07.2015

Как использовать маркер отмены, четко описано здесь: http://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx с рекомендуемым шаблоном.

Я сообщу пример, если страница упадет:

using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
    static void Main()
    {

        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;

        var task = Task.Factory.StartNew(() =>
        {

            // Were we already canceled?
            ct.ThrowIfCancellationRequested();

            bool moreToDo = true;
            while (moreToDo)
            {
                // Poll on this property if you have to do 
                // other cleanup before throwing. 
                if (ct.IsCancellationRequested)
                {
                    // Clean up here, then...
                    ct.ThrowIfCancellationRequested();
                }

            }
        }, tokenSource2.Token); // Pass same token to StartNew.

        tokenSource2.Cancel();

        // Just continue on this thread, or Wait/WaitAll with try-catch: 
        try
        {
            task.Wait();
        }
        catch (AggregateException e)
        {
            foreach (var v in e.InnerExceptions)
                Console.WriteLine(e.Message + " " + v.Message);
        }

        Console.ReadKey();
    }
}

Здесь есть еще более подробный пример: http://msdn.microsoft.com/en-us/library/dd537607(v=vs.110).aspx, но первого должно быть достаточно для вашего сценария.

person Saverio Terracciano    schedule 29.03.2014
comment
Я знаю, как используются токены cacellation, вопрос по конкретному сценарию. - person MaYaN; 29.03.2014