Заблокированный.инкремент по-прежнему не решает проблемы с отсутствием значения

#c# #multithreading

#c# #многопоточность

Вопрос:

Прямо сейчас я изучаю C # и в настоящее время изучаю потоковую обработку.

Вот простой пример добавления 1 к переменной несколько раз в разных потоках.

В книге предлагается, что я могу использовать Interlocked.increment(ref number) для замены числа = 1 в AddOne методе, поэтому значение будет заблокировано до тех пор, пока оно не будет обновлено в потоке. Таким образом, результат будет 1000, 2000, ..... 10000 таким, как ожидалось. Но мой вывод по-прежнему 999, 1999, 2999, ...... 9999 .

Только после того, как я раскомментирую Thread.Sleep(1000) строку, вывод будет правильным, но даже без Interlocked использованного.

Кто-нибудь может объяснить, что здесь происходит?

 static void Main(string[] args)
{
    myNum n = new myNum();

    for (int i = 0;i<10; Interlocked.Increment(ref i))
    {
        for(int a =1;a<=1000; Interlocked.Increment(ref a))
        {
            Thread t = new Thread( new ThreadStart( n.AddOne));
            t.Start();
        }

        //Thread.Sleep(1000);
        Console.WriteLine(n.number);
    }
}

class myNum
{            
    public int number = 0;

    public void AddOne()
    {
        //number  = 1;
        Interlocked.Increment(ref number);
    }  
}
  

Ответ №1:

Вы распечатываете значение до того, как все потоки завершат выполнение. Перед печатью необходимо объединить все потоки.

 for(int a = 0; a < 1000; a  )
  {
    t[a].Join();
  }
  

Вам нужно будет сохранить потоки в массиве или списке. Кроме того, вам не нужна инструкция interlocked ни в одном из циклов for. Все они выполняются только в одном потоке (главном потоке). Только код в addOne выполняется в нескольких потоках и, следовательно, нуждается в синхронизации.

Ответ №2:

Для меня немного странно, чего вы пытаетесь достичь с помощью этого кода. Вы используете Interlocked.Increment везде без явной необходимости в этом.

Interlocked.Increment требуется для доступа к значениям, к которым можно получить доступ из разных потоков. В вашем коде это только number , поэтому вам не требуется это для i и a , просто используйте как обычно i и a

Проблема, о которой вы просите, заключается в том, что вы просто не ждете, пока все запущенные вами потоки завершат свою работу. Взгляните на поток.Метод Join(). Вам придется подождать, пока все запущенные вами потоки завершат свою работу.

В этом простом тесте, с которым вы закончили, Thread.Sleep(1000); вы выполняете аналогичное ожидание, но неверно предполагать, что все потоки завершаются за 1000 мс, поэтому просто используйте Thread.Join() для этого.

Если вы измените свой AddOne() метод так, чтобы он начал выполняться дольше (например, добавьте Thread.Sleep(1000) к нему), вы заметите, что Thread.Sleep(1000); это больше не помогает.

Я предлагаю подробнее прочитать о пуле потоков против потоков. Также взгляните на шаблоны для параллельного программирования: понимание и применение параллельных шаблонов с .NET Framework 4

Комментарии:

1. На самом деле, поток. Sleep (1000) все равно будет работать нормально, если вы создадите поток. Sleep(10) в addOne (), потому что 10 << 1000.

2. Спасибо за все ответы, использование Interlocked. Увеличение в цикле for — это пример кода в книге, который, я думаю, тоже не является необходимым….. t.join(); очень полезен, он ожидает поток до его завершения. Я сделаю больше чтения, как вы предложили. Приветствия!

3. @Fantius, это зависит, поскольку одновременный запуск 10000 потоков может перегрузить систему, но на самом деле вы правы, на большинстве современных систем это почти не заметно. Я исправлю, спасибо.