Возможно ли грязное чтение из памяти при многопоточности?

В этом случае я определяю грязное чтение как чтение из памяти, когда оно в настоящее время записывается другим потоком.

Итак, если поток №1 записывает 100 в переменную, которую также может видеть поток №2, он записывает 50 в одну и ту же переменную, причем оба потока делают это в цикле.

Можно ли прочитать переменную и получить число, не равное ни 50, ни 100? Использование блокировки и т. Д. Для синхронизации.

Подробнее о моей настройке: Intel i3 CPU, я программирую на C #.

Вот пример того, что я имею в виду:

using System;
using System.Collections.Generic;
using System.Threading;

namespace Threading001
{
    class Program
    {
        static void Main(string[] args)
        {
            long min = long.MinValue;
            long max = long.MaxValue;
            object number = max;

            new Thread(() =>
            {
                long current2 = (long)number;
                if (current2 != min && current2 != max)
                {
                    Console.WriteLine("Unexpected number from thread 2: {0}.", current2);
                }
                number = min;
            }).Start();

            while (true)
            {
                long current = (long)number;
                if (current != min && current != max)
                {
                    Console.WriteLine("Unexpected number from thread 1: {0}.", current);
                }
                number = max;
            }

        }
    }
}

Я сделал число объектом, чтобы память выделялась в куче, а не в стеке, чтобы попытаться увеличить время задержки доступа к памяти. Кеширование процессора, вероятно, все равно остановит это.


person user1515024    schedule 31.12.2013    source источник
comment
Вы не указали нам, какой процессор, язык или типы данных вы используете ... что на самом деле не позволяет сказать.   -  person Jon Skeet    schedule 31.12.2013
comment
Зависит от архитектуры и размера / расположения записи. На какой платформе вы находитесь?   -  person Cory Nelson    schedule 31.12.2013


Ответы (4)


На самом деле вы пытаетесь здесь полагаться на несколько разных вещей.

Во-первых, вопрос атомарности. ECMA-335 утверждает:

Соответствующий интерфейс командной строки должен гарантировать, что доступ для чтения и записи к правильно выровненным ячейкам памяти, не превышающим размер собственного слова (размер типа native int), является атомарным (см. §I.12.6.2), когда все операции записи в ячейку выполняются. такого же размера. Атомарные записи не должны изменять никаких битов, кроме записанных. Если явное управление макетом (см. Раздел II (Управление макетом экземпляра)) не используется для изменения поведения по умолчанию, элементы данных, не превышающие естественный размер слова (размер собственного int), должны быть правильно выровнены. Ссылки на объекты должны обрабатываться так, как если бы они сохранялись с исходным размером слова.

Итак, для 32-битного целого числа все в порядке, а для 64-битного целого числа все в порядке, если вы работаете в 64-битной среде CLR ... при условии, что ваши переменные выровнены, что обычно .

Однако у вас также есть бокс - я не думаю, что это действительно вызовет здесь какие-либо проблемы, но это означает, что вы имеете дело с видимостью нескольких записей: одна для переменной number сам и один для данных внутри поля. Я считаю, что реализация .NET все еще безопасна из-за более надежных гарантий памяти, которые она предоставляет - я бы не хотел абсолютно гарантировать, что это безопасно в модели памяти ECMA.

Наконец, есть вопрос, является ли запись видимой или нет, что выходит за рамки атомарности. Если поток T1 изменяет значение int с 0 на 100, то поток T2 читает его, атомарность гарантирует, что T2 увидит либо 0, либо 100, но не какой-либо другой битовый шаблон, но обычно должен быть какой-то барьер памяти < / em>, чтобы гарантировать, что T2 действительно увидит новое значение вместо устаревшего. Это действительно сложная область - если вы хотите узнать больше, я предлагаю вам начать с сообщение в блоге Джо Даффи в 2007 г. и приступайте к работе.

Обратите внимание, что min, max и number все равно будут в куче, поскольку они были захвачены вашими лямбда-выражениями ... хотя стек / куча - это деталь реализации.

person Jon Skeet    schedule 31.12.2013

Это зависит от того, что это за переменная (и какой процессор и т. Д.), Но обычно:
Да, грязное чтение возможно.

person deviantfan    schedule 31.12.2013

Я не знаком с деталями вашего конкретного процессора, но в целом это зависит от того, является ли READ / WRITE атомарным, что, в свою очередь, зависит от архитектуры и того, как хранится переменная.

Если переменная имеет размер больше, чем размер слова ЦП, она не может быть атомарной.

Современный процессор может гарантировать атомарный доступ к выровненному адресу памяти; если он нуждается в дополнительном рассмотрении, если он не имеет аппаратной поддержки для смещенного доступа к памяти. Если неправильно согласованный доступ к памяти обрабатывается программным обеспечением, чтение или запись не будут атомарными: одна загрузка / сохранение может привести к фактически двум операциям. Одним из примеров является PowerPC / Linux, где ядро ​​обрабатывает неправильный доступ к памяти в обработчике исключений.

person tristan    schedule 31.12.2013

это все о потокобезопасности или нет, это зависит от типа данных той переменной, которую вы хотите прочитать, некоторые типы, такие как long, int, byte и т. д., являются потокобезопасными, и вы можете читать и записывать их в нескольких потоках.

вы можете найти больше информации здесь

http://msdn.microsoft.com/en-us/library/dd997305(v=vs.110).aspx

Являются ли примитивные типы данных в C # атомарными (потокобезопасными)?

Что такое потокобезопасность (C #)? (Строки, массивы, ...?)

person Jack Gajanan    schedule 31.12.2013