как исправить эту проблему Task/CancellationToken?

Я запускаю этот код;

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var task = DoSomething();
            Task.WaitAny(task);

            Console.WriteLine(task.Result);

            Console.Write("Press any key");
            Console.ReadKey();
        }
        static void WriteLine(string str, params string[] param)
        {
            Console.WriteLine(str, param);
            System.Diagnostics.Debug.WriteLine(str, param);
        }

        async static Task<string> DoSomething()
        {
            int timeout = 20;
            int count = 1;
            CancellationTokenSource cts;
            string output;                
            do
            {
                cts = new CancellationTokenSource(timeout);
                //does also happen with
                //output = await Task<string>.Run(() => test(count, timeout, cts.Token));
                output = await Task<string>.Run(() => test(count, timeout, cts.Token), cts.Token);

                if (cts.IsCancellationRequested)
                {
                    WriteLine(string.Format("Retry. Count value {2}, Test Sleep time {0}, Method timeout {1}", output, timeout, count));
                    timeout += 50;
                    count++;
                }
            } while (cts.IsCancellationRequested);

            return string.Format("Count value {2}, Test Sleep time {0}, Method timeout {1}", output, timeout, count);
        }

        async static Task<string> test(int count, int timeout, CancellationToken ct)
        {
            int sleep = 400 - (count * 5);
            await Task.Run(() => Task.Delay(sleep), ct);

            if (!ct.IsCancellationRequested)
            {
                WriteLine(string.Format("Succeed. Count value {2}, Test Sleep time {0}, Method timeout {1}", sleep, timeout, count)); 
            }
            return sleep.ToString();
        }
    }
}

и я получаю этот вывод

Retry. Count value 1, Test Sleep time 395, Method timeout 20
Retry. Count value 2, Test Sleep time 390, Method timeout 70
Retry. Count value 3, Test Sleep time 385, Method timeout 120
Retry. Count value 4, Test Sleep time 380, Method timeout 170
Retry. Count value 5, Test Sleep time 375, Method timeout 220
Retry. Count value 6, Test Sleep time 370, Method timeout 270
Retry. Count value 7, Test Sleep time 365, Method timeout 320
Succeed. Count value 8, Test Sleep time 360, Method timeout 370
Retry. Count value 8, Test Sleep time 360, Method timeout 370
Succeed. Count value 9, Test Sleep time 355, Method timeout 420

Как решить проблему на Count value 8 ?

Он должен сказать «Успех» и остановиться на этом.


person Fredou    schedule 25.12.2014    source источник
comment
К вашему сведению, я использую timeout = 50 и не могу воспроизвести проблему в Xamarin.   -  person tia    schedule 25.12.2014
comment
Вы проверяете CancellationToken внутри метода test, затем он переходит в отмененное состояние, затем вы проверяете CancellationToken внутри метода DoSomething.   -  person user4003407    schedule 25.12.2014
comment
Один вопрос, почему бы вам просто не использовать output = await test(count, timeout, cts.Token);?   -  person tia    schedule 25.12.2014


Ответы (1)


Проблема связана с состоянием гонки, поскольку IsCancellationRequested может измениться с false на true во время проверки "Успех" и "Повторить попытку". Трудно предложить правильное исправление, не зная фактического контекста этого кода.

person tia    schedule 25.12.2014