C # блокировка бесплатная проверка работоспособности кодирования - PullRequest
1 голос
/ 17 января 2011

ОБНОВЛЕНО : теперь используется коллекция только для чтения, основанная на комментариях ниже

Я считаю, что следующий код должен быть потокобезопасным кодом "без блокировки", но хочу убедиться, что яЯ не пропускаю что-то ...

public class ViewModel : INotifyPropertyChanged
{
   //INotifyPropertyChanged and other boring stuff goes here...

   private volatile List<string> _data;
   public IEnumerable<string> Data
   {
      get { return _data; }
   }

   //this function is called on a timer and runs on a background thread
   private void RefreshData()
   {
      List<string> newData = ACallToAService();
      _data = newData.AsReadOnly();
      OnPropertyChanged("Data"); // yes, this dispatches the to UI thread
   }
}

В частности, я знаю, что могу использовать lock(_lock) или даже Interlocked.Exchange(), но я не верю, что в этом есть необходимостьдело.Ключевого слова volatile должно быть достаточно (чтобы убедиться, что значение не кэшируется), нет?Может кто-нибудь, пожалуйста, подтвердите это, или дайте мне знать, что я не понимаю о потоках:)

Ответы [ 4 ]

7 голосов
/ 17 января 2011

Я понятия не имею, "безопасно" это или нет; это зависит именно от того, что вы подразумеваете под «безопасным». Например, если вы определите «безопасный» как «последовательное упорядочение всех энергозависимых записей гарантированно будет наблюдаться из всех потоков», то ваша программа будет не гарантированно «безопасной» на всех аппаратных средствах.

Лучшая практика здесь - использовать замок, если у вас нет веских причин не делать этого. Какова ваша чрезвычайно веская причина написать этот рискованный код?

ОБНОВЛЕНИЕ: Моя точка зрения такова, что код с низкой блокировкой или без блокировки чрезвычайно рискован и что на самом деле это понимают лишь небольшое количество людей в мире. Позвольте мне дать Вы, например, от Джо Даффи:

// deeply broken, do not use!
class Singleton {
    private static object slock = new object();
    private static Singleton instance;
    private static bool initialized;
    private Singleton() {}
    public Instance {
        get {
            if (!initialized) {
                lock (slock) {
                    if (!initialized) {
                        instance = new Singleton();
                        initialized = true;
                    }
                }
            }
            return instance;
        }
    }
}

Этот код не работает; для правильной реализации компилятора C # совершенно правильно написать программу, которая возвращает нуль для экземпляра. Вы видите, как ? Если нет, то вам не нужно заниматься программированием без блокировки или без блокировки; Вы ошибетесь .

Я не могу понять это сам; это ломает мне мозг. Вот почему я стараюсь никогда не заниматься программированием с низкой блокировкой, которое каким-либо образом отличается от стандартных методов, которые были проанализированы экспертами.

5 голосов
/ 17 января 2011

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

Существует также состояние гонки, которое может привести к потере данных:

 obj.Data.Add(value);

Здесь значение можно легко отбросить.

Я бы использовал неизменную (только для чтения) коллекцию.

0 голосов
/ 11 февраля 2013

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

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

Например, это будет безопасно:

foreach (var item in x.Data)
{
   // do something with item
}

И это было бы безопасно (при условии, что JIT не позволено оптимизировать локальное, что, как я думаю, имеет место ):

var data = x.Data;
var item1 = FindItem(data, a);
var item2 = FindItem(data, b);
DoSomething(item1, item2);

Вышеуказанные два могут воздействовать на устаревшие данные, но это всегда будут согласованные данные. Но это не обязательно будет безопасно:

var item1 = FindItem(x.Data, a);
var item2 = FindItem(x.Data, b);
DoSomething(item1, item2);

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

Проблема была бы хуже с более широким интерфейсом; например. если данные выставлены IList<T>, вам также придется следить за согласованностью операций Count и индексатора.

0 голосов
/ 17 января 2011

Я думаю, что если у вас есть только два потока, как вы описали, ваш код верен и безопасен. А также вам не нужен этот изменчивый, он здесь бесполезен.

Но, пожалуйста, не называйте это "потокобезопасным", поскольку это безопасно только для двух ваших потоков, использующих его по-особенному.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...