Как гарантируется актуальность CancellationTokens?

Microsoft приводит этот пример использования CancellationToken в .NET 4.

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

Однако насколько я понимаю, если переменная изменяется в одном потоке, другой поток может не получить измененное значение из-за кэширование. И поскольку CancellationToken в основном потоке отменяется, как поток Task может быть уверен, что CancellationToken, который он проверяет, действительно актуален?

Почему Task не может читать кэшированное значение токена?

Примечание. Я задаю этот вопрос, потому что мне интересно, нужно ли мне, чтобы мои CancellationToken переменные экземпляра были volatile.


person Eric    schedule 07.08.2012    source источник


Ответы (1)


Это обрабатывается внутри CancellationTokenSource. Частная переменная, используемая для отслеживания состояния CTS, помечена volatile, что предотвращает внутренняя проверка состояния от устаревшего.

Моя мотивация задать этот вопрос связана с вопросом, нужны ли мне переменные экземпляра CancellationToken, чтобы они были изменчивыми.

Вам не нужно этого делать, так как проверка выполняется внутри и уже выполняется должным образом для вас.

По сути, когда вы создаете CancellationToken из CancellationTokenSource, токен содержит ссылку на исходный источник. Эта ссылка никогда не может измениться, поэтому вызов ThrowIfCancellationRequested проверяет состояние источника внутри. Поскольку исходное состояние само по себе volatile, оно никогда не бывает "устаревшим" данными.

person Reed Copsey    schedule 07.08.2012
comment
Кроме того, CancellationTokenSource является ссылочным типом. Использование volatile для переменной CancellationTokenSource делает только изменение/чтение ссылки на это поле volatile, чего не происходит, когда TPL делает что-либо с объектом CancellationTokenSource. Будьте уверены, внутренние компоненты CancellationTokenSource являются потокобезопасными, как описывает Рид. - person Peter Ritchie; 07.08.2012
comment
@PeterRitchie Я думаю, что ОП задавался вопросом, нужно ли ему объявлять свой CancellationToken (который является структурой) как volatile, а не CTS ... Но да, поскольку токен неизменяем и содержит ссылку на CTS, он просто работает - person Reed Copsey; 07.08.2012
comment
Хм, я, должно быть, запутался с volatile, поскольку volatile нельзя использовать в структуре, и поэтому поле CancellationToken никогда не может быть украшено volatile. Даже если бы CancellationToken не было неизменяемым, любые изменения в типе значения можно увидеть только в этой копии, хотя копию ct никогда не следует делать в опубликованном коде. - person Peter Ritchie; 07.08.2012
comment
@PeterRitchie Да - я считаю, что это было недоразумение со стороны ОП - это не сработало бы, но это было задано в вопросе. - person Reed Copsey; 07.08.2012