C# Является ли присвоение типа значения атомарным?

Считается ли присвоение типа значения атомарным в.Net?

Например, рассмотрим следующую программу:

struct Vector3
{
    public float X { get; private set; }
    public float Y { get; private set; }
    public float Z { get; private set; }


    public Vector3(float x, float y, float z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public Vector3 Clone()
    {
        return new Vector3(X, Y, Z);
    }

    public override String ToString()
    {
        return "(" + X + "," + Y + "," + Z + ")";
    }
}

class Program
{
    private static Vector3 pos = new Vector3(0,0,0);

    private static void ReaderThread()
    {
        for (int i = 0; i < int.MaxValue; i++)
        {
            Vector3 v = pos;
            Console.WriteLine(v.ToString());
            Thread.Sleep(200);
        }

    }

    private static void WriterThread()
    {
        for (int i = 1; i < int.MaxValue; i++)
        {
            pos = new Vector3(i, i, i);
            Thread.Sleep(200);
        }
    }


    static void Main(string[] args)
    {
        Thread w = new Thread(WriterThread);
        Thread r = new Thread(ReaderThread);

        w.Start();
        r.Start();
    }
}

Может ли такая программа страдать от гонки данных высокого уровня? Или даже гонка данных?

Здесь я хочу знать: есть ли вероятность, что v будет содержать:

  • Значения мусора из-за возможной гонки данных
  • Смешанные компоненты X, Y или Z, которые относятся как к положению до назначения, так и к положению после назначения. Например, если pos = (1,1,1) и затем назначено pos, новое значение (2,2,2) может v = (1,2,2)?

1 ответ

Решение

Структуры являются типами значений. Если вы присвоите структуру переменной / полю / параметру метода, все содержимое структуры будет скопировано из исходного хранилища в место хранения параметра переменной / поля / метода (место хранения в каждом случае равно размеру сама структура).

Копирование структуры не гарантируется как атомарная операция. Как написано в спецификации языка C#:

Атомарность переменных ссылок

Чтение и запись следующих типов данных являются атомарными: типы bool, char, byte, sbyte, short, ushort, uint, int, float и reference. Кроме того, чтение и запись перечислимых типов с базовым типом в предыдущем списке также являются атомарными. Чтение и запись других типов, включая long, ulong, double и decimal, а также определяемые пользователем типы, не гарантированно являются атомарными. Помимо библиотечных функций, разработанных для этой цели, нет гарантии атомарного чтения-изменения-записи, например, в случае увеличения или уменьшения.


Так что да, может случиться так, что в то время как один поток находится в процессе копирования данных из хранилища структур, появляется другой поток и начинает копировать новые данные из другой структуры в это хранилище. Таким образом, поток, копируемый из места хранения, может в итоге скопировать смесь старых и новых данных.


В качестве примечания, ваш код может также страдать от других проблем параллелизма из-за того, как один из ваших потоков пишет в переменную и как переменная используется другим потоком. (Ответ пользователя acelent на другой вопрос объясняет это довольно хорошо с технической точки зрения, поэтому я просто буду ссылаться на него: /questions/30059966/c-volatile-peremennaya-pamyat-ograzhdeniya-vs-keshirovanie/30059968#30059968) Вы можете избежать таких проблем, инкапсулируя любой доступ к таким переменные "пересечения потока" в lock блок. В качестве альтернативы lock и что касается основных типов данных, вы также можете использовать методы, предоставляемые Interlocked класс для доступа к переменным / полям, пересекающим потоки, безопасным для потока способом (чередование между lock а также Interlocked хотя методы для одной и той же переменной пересечения потока не очень хорошая идея).

Другие вопросы по тегам