2016-12-30 12 views
1

Я следующий фрагмент кода:Что такое использование Отмена маркера здесь

using (var cancelSource = new CancellationTokenSource()) 
{ 
    Task[] tasks = null; 

    var cancelToken = cancelSource.Token; 
    tasks = new[] 
    { 
     Task.Run(async() => await ThrowAfterAsync("C", cancelToken, 1000)) //<--- 
    }; 
    await Task.Delay(howLongSecs * 1000); // <--- 
    cancelSource.Cancel(); 
    await Task.WhenAll(tasks); 
} 

Где ThrowAfterAsync есть это:

private async Task ThrowAfterAsync(string taskId, CancellationToken cancelToken, int afterMs) 
{ 
    await Task.Delay(afterMs, cancelToken); 
    var msg = $"{taskId} throwing after {afterMs}ms"; 
    Console.WriteLine(msg); 
    throw new ApplicationException(msg); 
} 

Resharper предлагает, что я могу использовать перегрузку Task.Run() с маркером отмены:

Task.Run(async() => await ThrowAfterAsync("C", cancelToken, 1000), cancelToken) 

Но почему? В чем преимущество этого в первой версии, без маркера отмены в качестве параметра?

+0

Ваш «кусок кода» кажется, что у него есть некоторые ошибки; что произойдет первым, броском или отменой? –

+0

Кроме того, вам не нужно принимать все советы, которые дает вам Resharper. Я часто этого не делаю. –

+0

В моем коде есть блок try/catch, который я только что удалил. –

ответ

4

В этом конкретном случае нет смысла. В общем, вы хотели бы сделать то, что он предлагает, потому что, передав токен Task.Run, он может избежать даже планирования операции в первую очередь, если токен отменен до того, как операция даже имеет шанс на запуститься, но в в вашем случае вы создаете токен, а вы знаете он не будет отменен при запуске операции.

Но причина, что вам не нужно, чтобы передать маркер Task.Run потому, что код, начинающийся, что задача является операцией отвечает за отмену маркеров, и поэтому он знает, что маркер пока не отменен. Обычно вы принимаете токен из другого места, и вы не знаете, когда/когда он был отменен.

Все, что сказал, нет оснований даже использовать Task.Runвообще. Вы можете просто написать:

tasks = new[] { ThrowAfterAsync("C", cancelToken, 1000) }; 

Он будет иметь такое же поведение, но без надобности начинать новую ветку просто начать асинхронную операцию.

Далее, ваш код никогда не вернется менее чем за howLongSecs секунд, даже если операция закончится до этого, из-за того, как вы структурировали свой код. Вы должны просто предоставить тайм-аут источнику токена отмены, и пусть он позаботится об отмене токена в нужное время, он не задержит остальную часть вашего метода, если операция закончится до отмены, поэтому весь ваш метод может просто быть записано как:

using (var cancelSource = new CancellationTokenSource(Timespan.FromSeconds(howLongSecs))) 
{ 
    await ThrowAfterAsync("C", cancelToken, 1000) 
} 
+0

Спасибо за быстрый и описательный ответ! –

+0

Вот как выглядел оригинальный код (например) (http://prntscr.com/dpslkv) с указанными вами изменениями. Если это объясняет некоторые странные вещи в коде, я думал, что они избыточны, но, видимо, это не так. –

+0

@ dB.Employee Выполнение нескольких задач, переданных 'WhenAll', по-видимому, фактически используется, поскольку у вас есть две фактические операции. Ничего из этого не требуется, и пустой «улов» активно вреден. – Servy

2

Resharper видит, что вы используете метод (Task.Run), который имеет перегрузку, которая принимает CancellationToken, у вас есть экземпляр CancellationToken в объеме, но не использовать эту перегрузку, которая принимает маркер. Он не выполняет каких-либо обширных анализов вашего кода - это так просто. Вы можете легко убедиться в этом с этим кодом:

class Program { 
    static void Main() { 
     CancellationToken ct; 
     Test("msg"); // will suggest to pass CancellationToken here 
    } 

    private static void Test(string msg) { 

    } 

    private static void Test(string msg, CancellationToken ct) { 

    } 
} 

Да сам код является странным и вам не нужно обернуть асинхра в Task.Run на всех, но я не буду трогать, что, поскольку вы спросили, почему Resharper предполагает, что.