многопоточность инкремент
От: Аноним  
Дата: 04.06.13 15:32
Оценка: 3 (1)
Есть статическая переменая int x;
Есть метод в котором один оператор x++;
Запускаю 100 потоков которые вызывают данный метод получаю результат 100 или 99.

То что нужно использовать синхронизацию это понятно, хотелось бы понять почему именно только 2 результата получается 100 и 99
а не 98, 101 и т.п.
Re: многопоточность инкремент
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 04.06.13 15:45
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Есть статическая переменая int x;

А>Есть метод в котором один оператор x++;
А>Запускаю 100 потоков которые вызывают данный метод получаю результат 100 или 99.

А>То что нужно использовать синхронизацию это понятно, хотелось бы понять почему именно только 2 результата получается 100 и 99

А>а не 98, 101 и т.п.

101 быть не может, если ты вызываешь инкремент всего 100 раз. А так вероятность коллизии скорее всего маленькая. Пока запускаются новые потоки (долгая операция), другие уже отработали.
Re: многопоточность инкремент
От: samius Япония http://sams-tricks.blogspot.com
Дата: 04.06.13 15:53
Оценка: 4 (1) +1
Здравствуйте, Аноним, Вы писали:

А>Есть статическая переменая int x;

А>Есть метод в котором один оператор x++;
А>Запускаю 100 потоков которые вызывают данный метод получаю результат 100 или 99.

А>То что нужно использовать синхронизацию это понятно, хотелось бы понять почему именно только 2 результата получается 100 и 99

А>а не 98, 101 и т.п.
100 получается понятно почему. Это в случае когда 100 потоков выполнили оператор x++ и не пересеклись во времени, не выполняли эту операцию одновременно.
99 — тоже понятно почему. Два потока без синхронизации значение x, оба инкрементировали и записали оба примерно одновременно.
По поводу 101- непонятно, откуда должно взяться 101, ведь потоков 100 и каждый инкрементирует лишь один раз. Вот если бы каждый прибавлял 2, а потом вычитал один — тогда 101 было бы возможно.

Для того что бы увидеть больший разбег от ожидаемого числа, предлагаю зациклить каждый поток до 10000, а потом сравнить результат в x с 1000000.
Re: многопоточность инкремент
От: Аноним  
Дата: 04.06.13 17:22
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Есть статическая переменая int x;

А>Есть метод в котором один оператор x++;
А>Запускаю 100 потоков которые вызывают данный метод получаю результат 100 или 99.

А>То что нужно использовать синхронизацию это понятно, хотелось бы понять почему именно только 2 результата получается 100 и 99

А>а не 98, 101 и т.п.

Еще вопрос, если запускать в студии в Release конфигурации, то ситуация воспроизводится хорошо.
Если запустить просто exe файл release без студии , то не воспроизводится, даже если увеличить на порядок количество потоков.
Студия ставит синхронизацию не явно ?
Re: многопоточность инкремент
От: 0x7be СССР  
Дата: 04.06.13 17:24
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>То что нужно использовать синхронизацию это понятно, хотелось бы понять почему именно только 2 результата получается 100 и 99

А>а не 98, 101 и т.п.
Сколько ядер?
Re[2]: многопоточность инкремент
От: Аноним  
Дата: 04.06.13 17:30
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Аноним, Вы писали:


А>>То что нужно использовать синхронизацию это понятно, хотелось бы понять почему именно только 2 результата получается 100 и 99

А>>а не 98, 101 и т.п.
0>Сколько ядер?

Core i5-2410M
Re[3]: многопоточность инкремент
От: 0x7be СССР  
Дата: 04.06.13 17:32
Оценка:
Здравствуйте, Аноним, Вы писали:

0>>Сколько ядер?

А>Core i5-2410M
Процессор двухъядерный.
Есть гипотеза, что связано с этим.
Интереса ради, попробуй найти 6-8 ядерную тачку и запусти тест на ней.
Re[4]: многопоточность инкремент
От: Аноним  
Дата: 04.06.13 17:53
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Аноним, Вы писали:


0>>>Сколько ядер?

А>>Core i5-2410M
0>Процессор двухъядерный.
0>Есть гипотеза, что связано с этим.
0>Интереса ради, попробуй найти 6-8 ядерную тачку и запусти тест на ней.

А есть идея почему вот такой код, ( специально замудрил y и sin чтобы оптимизатору было сложнее и вероятность ошибок повысить, так по сути это тот же x++ );
в Release сборке работает без сбоев, всегда выдает ровно 1000 если запускать без студии.

    class Program
    {
        static int x;
        static int y;

        static void Main(string[] args)
        {

            while(true)
            {
                var t = new List<Thread>();
                for (int i = 0; i < 1000; i++)
                {
                    var th = new Thread(new ParameterizedThreadStart(o => { y = x + (int)Math.Sin(Math.PI/2);  x = 1 + y; x = x - 1; }));
                    t.Add(th);                   
                }

                for (int i = 0; i < t.Count; i++)
                    t[i].Start();

                
                for (int i = 0; i < t.Count; i++)
                    t[i].Join();

                if (x != 1000)
                    Console.Write(x + " ");

                x = 0;
            }
        }
    }



При этом если заглянуть в ildasm для релизного exe файла то там для расчета генерится такой код, явно не оптимизированных/не атомарный и без синхронизаций
.method private hidebysig static void  '<Main>b__0'(object o) cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Размер кода:       51 (0x33)
  .maxstack  8
  IL_0000:  ldsfld     int32 ConsoleApplication1.Program::x
  IL_0005:  ldc.r8     1.5707963267948966
  IL_000e:  call       float64 [mscorlib]System.Math::Sin(float64)
  IL_0013:  conv.i4
  IL_0014:  add
  IL_0015:  stsfld     int32 ConsoleApplication1.Program::y
  IL_001a:  ldc.i4.1
  IL_001b:  ldsfld     int32 ConsoleApplication1.Program::y
  IL_0020:  add
  IL_0021:  stsfld     int32 ConsoleApplication1.Program::x
  IL_0026:  ldsfld     int32 ConsoleApplication1.Program::x
  IL_002b:  ldc.i4.1
  IL_002c:  sub
  IL_002d:  stsfld     int32 ConsoleApplication1.Program::x
  IL_0032:  ret
} // end of method Program::'<Main>b__0'
Re[5]: многопоточность инкремент
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.06.13 09:21
Оценка:
Здравствуйте, Аноним, Вы писали:
А>А есть идея почему вот такой код, ( специально замудрил y и sin чтобы оптимизатору было сложнее и вероятность ошибок повысить, так по сути это тот же x++ );
А>в Release сборке работает без сбоев, всегда выдает ровно 1000 если запускать без студии.
Потому что в Release нет лишнего кода в математике, и поток номер N успевает закончится до того, как отработает thread.Start для потока номер N+1.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: многопоточность инкремент
От: Аноним  
Дата: 05.06.13 16:23
Оценка: :)
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Аноним, Вы писали:

А>>А есть идея почему вот такой код, ( специально замудрил y и sin чтобы оптимизатору было сложнее и вероятность ошибок повысить, так по сути это тот же x++ );
А>>в Release сборке работает без сбоев, всегда выдает ровно 1000 если запускать без студии.
S>Потому что в Release нет лишнего кода в математике, и поток номер N успевает закончится до того, как отработает thread.Start для потока номер N+1.


Вот добавил ManualResetEvent и сигнализирую начало расчетов когда для всех потоков вызовется Start , %ошибок сразу существенно возрос, причем стали попадаться и 97 , 98.
Но еще появился вопрос, если поставить между Start() и e.Set() Thread.Sleep(1000) то ошибки резко пропадают, с чем это связанно ?
Потоки засыпают и потом не одновременно просыпаются ?

    class Program
    {
        static int x;
      

        static ManualResetEvent e = new ManualResetEvent(true);


        static void Job(object o)
        {
            e.WaitOne();
            
            x++;
        }


        static void Main(string[] args)
        {

            while(true)
            {
                var t = new List<Thread>();
                for (int i = 0; i < 100; i++)
                {
                    var th = new Thread(new ParameterizedThreadStart(Job));
                    t.Add(th);                   
                }

                e.Reset();
                
                for (int i = 0; i < t.Count; i++)
                    t[i].Start();


                //Thread.Sleep(1000);

                e.Set();
              

                for (int i = t.Count-1; i >=0; i--)
                    t[i].Join();

     
                    Console.Write(x + " ");

                x = 0;
            }
        }
    }
Re: многопоточность инкремент
От: igor-booch Россия  
Дата: 06.06.13 06:13
Оценка:
Инкремент x++ это
x = x + 1

Несмотря на простату этого statement'а для процессора он состоит из трех шагов:

1) считать значение переменной x
2) прибавить единицу к значению полученному на шаге 1
3) присвоить переменной x значение полученное на шаге 2

Есть вероятность что два (или даже более (что менее вероятно)) потока одновременно считают одно и тоже текущее значение переменной x.
Далее на шаге 2 получат одно тоже инкрементированное значение, и соответственно на шаге 3 запишут одно и тоже значение в переменную x.
Получится что 1 (или даже более (что менее вероятно)) поток сработает в холостую.

Теоретически таких коллизий может случиться несколько.
Обозначим за n число потоков. У тебя n = 100.
Итоговое значение x может быть только меньше либо равно n.
Обозначим разницу между n и итоговым значением x за d.
Чем больше n, тем больше может быть d.
Так как чем больше потоков, тем больше коллизий может произойти.
А сколько конкретно произойдет коллизий вопрос случая.
Естественно большой количество коллизий менее вероятно, чем малое.
Могу предположить что влияние здесь оказывает компиляция под Release с оптимизациями. В этом случае код x = x + 1 становится более оптимизированным, то есть занимает меньшее число инструкций процессора и поэтому вероятность коллизий меньше, чем с не оптимизированным кодом. Иными словами операция x = x + 1 становится более атомарной, но полную атомарность может гарантировать только Interlocked.Increment или другие примитивы синхронизации (например Monitor).
Еще могу предположить что увеличение количества ядер CPU увеличит вероятность коллизий.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[2]: многопоточность инкремент
От: Аноним  
Дата: 06.06.13 07:19
Оценка: +2 :)
Здравствуйте, igor-booch, Вы писали:

Хорошая задача в контексте http://rsdn.ru/forum/dotnet/5176745.1
Автор: igor-booch
Дата: 22.05.13


Интересно, ConcurentIncrementor на 5 ядрах обгонит-ли x++ в одном потоке
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.