Вы не говорите, сколько потоков вы использовали, но я предполагаю, что два - если бы вы работали с четырьмя потоками, я ожидал, что разблокированная версия получится с результатом, который достаточно близок к 1/4 от одноговерсия с корректным результатом.
Когда вы не используете lock
, ваш quad-proc компьютер выделяет поток каждому ЦП (этот оператор исключает присутствие других приложений, которые также будут запланированы ввключите, для простоты) и они бегут на полной скорости, без помех друг другу.Каждый поток получает значение из памяти, увеличивает его и сохраняет обратно в память.Результат перезаписывает то, что там, что означает, что, поскольку у вас есть 2 (или 3, или 4) потока, работающие на полной скорости в то же время, некоторые из приращений, сделанные потоками на других ваших ядрах, эффективно отбрасываются.Таким образом, ваш конечный результат ниже, чем тот, который вы получили из одного потока.
Когда вы добавляете оператор lock
, это говорит CLR (это похоже на C #?), Чтобы гарантировать, что только один поток в любомдоступное ядро, может выполнить этот код.Это критическое изменение по сравнению с описанной выше ситуацией, поскольку несколько потоков теперь мешают друг другу, даже если вы понимаете, что этот код не является потокобезопасным (достаточно близко к нему, чтобы быть опасным).Эта неправильная сериализация приводит (как побочный эффект) к тому, что последующее приращение выполняется одновременно реже - поскольку подразумеваемая разблокировка требует дорогостоящего, с точки зрения этого кода и вашего многоядерного ЦП, по крайней мере, пробуждения любых потоков, которые былижду блокировки.Эта многопоточная версия также будет работать медленнее, чем однопоточная, из-за этих издержек.Потоки не всегда делают код быстрее.
Пока все ожидающие потоки выходят из своего состояния ожидания, поток, освобождающий блокировку, может продолжать работать в своем временном интервале и часто получает, увеличивает и сохраняет переменную до пробуждающиеся потоки получают возможность взять копию переменной из памяти для своего собственного увеличения.Таким образом, вы получите конечное значение, близкое к однопоточному, или то, что вы получите, если вы lock
увеличите шаг внутри цикла.
Проверьте Interlocked класс для аппаратного уровня для атомарной обработки переменных определенного типа.