Подтвердить что ты не робот

Является ли свойство string самотечным?

Строка в С# неизменяема и небезопасна. Но что, когда у вас есть общедоступное свойство getter? Вот так:

public String SampleProperty{
    get;
    private set;
}

Если у нас есть два потока, а первый вызывает "get", а второй вызывает "set" в "то же самое" время, что произойдет?

IMHO набор должен сделать блокировку безопасной для потока следующим образом:

private string sampleField;
private object threadSafer = new object();

public String SampleProperty{
    get{ return this.sampleField; }
    private set{
        lock(threadSafer){
            sampleField = value;
        }
    }
 }
4b9b3361

Ответ 1

В большинстве ответов используется слово "атомный", как будто необходимы все атомарные изменения. Обычно это не так.

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

Обычно вы хотите, чтобы поток чтения отображал последнее значение переменной /property. То, что не гарантируется атомарностью. В качестве краткого примера, здесь плохой способ остановить поток:

class BackgroundTaskDemo
{
    private bool stopping = false;

    static void Main()
    {
        BackgroundTaskDemo demo = new BackgroundTaskDemo();
        new Thread(demo.DoWork).Start();
        Thread.Sleep(5000);
        demo.stopping = true;
    }

    static void DoWork()
    {
         while (!stopping)
         {
               // Do something here
         }
    }
}

DoWork вполне может зацикливаться навсегда, несмотря на то, что запись в логическую переменную является атомарной - нет ничего, чтобы остановить JIT от кеширования значения stopping в DoWork. Чтобы исправить это, вам нужно либо заблокировать, внести переменную volatile, либо использовать явный барьер памяти. Все это относится и к свойствам строки.

Ответ 2

Поле get/set ссылочного типа (ldfld/stfld) является (IIRC) гарантированным атомарным, поэтому здесь не должно быть риска коррупции. Поэтому он должен быть потокобезопасным от , который, но лично я заблокировал данные на более высоком уровне - т.е.

lock(someExternalLock) {
    record.Foo = "Bar";
}

или возможно:

lock(record.SyncLock) {
    record.Foo = "Bar";
}

Это позволяет сделать несколько чтений/обновлений одним и тем же объектом как атомную операцию, так что другие потоки не смогут получить недопустимое состояние объекта

Ответ 3

Установка строки - это атомная операция, то есть вы либо получите новую строку, либо старую строку, вы никогда не получите мусор.

Если вы выполняете какую-либо работу, например.

obj.SampleProperty = "Dear " + firstName + " " + lastName;

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

Если, однако, ваш код конкатенации строк является самореляционным, например.

obj.SampleProperty += obj.SampleProperty + "a";

а где в другом потоке есть

obj.SampleProperty = "Initial String Value";

Затем вам понадобится блокировка.

Предположим, вы работаете с int. Если вы назначаете int, и любое значение, которое вы получаете из int, является допустимым, тогда вам не нужно его блокировать.

Однако, если int ведет подсчет количества виджетов, обработанных двумя или более потоками, чтобы счет был точным, вам нужно заблокировать int. Это та же ситуация для строк.

У меня такое чувство, что я не очень хорошо объяснил это, надеюсь, что это поможет.

Спасибо

BW

Ответ 4

Это поточно-безопасный без необходимости блокировки. Строки являются ссылочными типами, поэтому изменяется только ссылка на строку. Литературам типа гарантируется атомарная (Int32 на 32-битных системах и Int64 на 64-разрядной версии).

Ответ 5

Второй образец кода определенно не прав, поскольку блокировки имеют желаемый эффект, когда они используются во всех местах, где доступна переменная (как для get, так и для набора), поэтому для get также потребуется блокировка.

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

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