Как доказать, что блокировка удерживается и задерживается в узком цикле, может не быть получена ожидающим потоком - PullRequest
0 голосов
/ 31 мая 2018

... но был странный результат.

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

Я обнаружил, что я думал, что это потенциальная ошибка, где есть Task.Delay (1) .Wait () внутри блокировки повторапоток и мой «аргумент» / теория заключались в том, что хотя цикл повторных попыток освобождает блокировку, он все же может получить блокировку до того, как записывающий поток сможет.

Поэтому я решил посмотреть, смогу ли я быстро воспроизвести код (скорееплохо):

namespace TaskDelay
{
    class Program
    {
        static Dictionary<int, string> dict = new Dictionary<int, string>();
        static  BlockingCollection<int> bc = new BlockingCollection<int>();
        static  ManualResetEvent resetEvent = new ManualResetEvent(false);
        static  ManualResetEvent nresetEvent = new ManualResetEvent(false);
        static void Main(string[] args)
        {
            new Program().Run();

            Console.Read();
        }

        public void Run()
        {
            var t = new Thread(RetryThread);
            t.Start();
            var v = new Thread(TryAdd);
            v.Start();
            bc.Add(20);
            bc.Add(21);
            resetEvent.Set();

        }

        public void RetryThread()
        {
            resetEvent.WaitOne();
            Console.WriteLine("Starting RetryLoop");
            bool done = false;
            nresetEvent.Set();
            while (!done)
            {
                done = RetryLoop(21);
            }

        }
        public bool RetryLoop(int x)
        {
            Console.WriteLine($"In Retry loop {x}");

            for (int i = 0; i < 5; i++)
            {
                lock (dict)
                {

                    Console.WriteLine($"Entering Retry loop {x}. Count {i+1}");
                    if (!dict.ContainsKey(x))
                    {
                        Task.Delay(1).Wait();//Change to Thread.Sleep(1) 
                        continue;
                    }
                    Console.WriteLine($"eXITING Retry loop {x}");
                    return true;
                }
            }
            return false;
        }

        void TryAdd()
        {
            Console.WriteLine("Starting TryAdd");
            nresetEvent.WaitOne();

            for (int i = 0; i < 2000; i++)
            {
                if (i == 1500)
                    break;
                Thread.Sleep(1);
            }
            Console.BackgroundColor = ConsoleColor.Red;
            Console.WriteLine("Exited loop");
            Console.BackgroundColor = ConsoleColor.Black;
            lock (dict)
            {
                Console.BackgroundColor = ConsoleColor.Red;
                Console.WriteLine("Entered lock try add");
                dict[21] = "hello";
                Console.WriteLine("Exit lock try add");
                Console.BackgroundColor = ConsoleColor.Black;
            }
        }
        }
    }

Я попробовал 3 теста в RetryLoop со следующими результатами:

  1. Использование Task.Delay (1) .Wait () в блокировке.
  2. Использовать Thread.Sleep (1) в блокировке
  3. Нет задержки в блокировке

В случае 1: записывающий поток получает блокировку быстрее.Мгновенно, как только поток записи выходит из своего простого цикла счетчика.

Test case 1

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

Test case 2

Обратите внимание на разрыв в двух изображениях. Мой вопрос: почему случай 1 значительно быстрее?Может быть, тогда я ошибался ...

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

...