Безопасно ли назначение объектов? - PullRequest
5 голосов
/ 06 октября 2011

Безопасен ли этот поток?В частности, возможно ли для метода GetMyObject() вернуть значение null?Я понимаю, что два потока могут получить различный экземпляр MyObject, но меня это не волнует. Я просто хочу убедиться, что можно предположить, что GetMyObject() никогда не вернет ноль.

   class Foo {
        private static MyObject obj;
        public static MyObject GetMyObject() {
            MyObject o = obj;
            if(o == null) {
                o = new MyObject();
                obj = o;
            }
            return o;
        }
        public static void ClearMyObject() {
            obj = null;
        }
    }

    class MyObject {}

Ответы [ 4 ]

6 голосов
/ 06 октября 2011

Безопасен ли этот поток?

Нет.

Возможно ли, чтобы метод GetMyObject () возвратил ноль?

Нет.

Метод гарантированно никогда не вернет ноль.И все операции чтения и записи гарантированно будут атомарными.Однако потокам не гарантируется чтение последней версии статического поля obj , а потокам не гарантируется согласованное представление последовательности изменений в obj .Произвольно много потоков могут мчаться и наблюдать различные значения obj .Я бы не стал считать этот код «потокобезопасным», но, возможно, у вас другое определение «потокобезопасности».Это проблема с заданием этого вопроса;не существует стандартного определения термина, с которым все достоверно соглашаются.

5 голосов
/ 06 октября 2011

GetMyObject () никогда не может возвращать ноль.Самый простой способ убедиться в этом - заметить, что 'o' - локальная переменная, поэтому никто другой не может повлиять на нее.

4 голосов
/ 06 октября 2011

Хорошо, давайте рассмотрим это:

public static MyObject GetMyObject() {
        MyObject o = obj;
        if(o == null) {
            o = new MyObject();
            obj = o;
        }
        return o;

}

Существует только одно return утверждение. Единственный способ, которым этот метод может произвести возвращаемое значение null, - это если единственный return оператор return o имеет o == null, равный true, когда он выполняется.

Если o равно null при выполнении return o, это означает, что мы вышли из блока if с o как null. Единственный способ, которым мы можем выйти из блока o с o как null, это если o == null был true, когда проверялось условное значение для блока if (если o == null равно false , тогда o != null имеет значение true, и поскольку o является локальной переменной, на него не может влиять никакой другой поток. Но тогда o == null, будучи true, подразумевает, что мы окажемся внутри блока if и теперь, когда конструктор вызов o = new MyObject() возвращает, мы гарантируем, что o не является null. Второй оператор в блоке if, obj = o, не влияет на значение o. Опять же, поскольку o локальная переменная, не имеет значения, если через этот путь к коду записано несколько потоков: каждый поток имеет свой собственный o, и ни один другой поток не может касаться любого другого потока o.

Поэтому, независимо от того, o == null равен true или false, мы получим o == null, равную false, когда блок if завершится.

Следовательно, этот метод гарантированно возвращает ненулевое значение.

Я просто хочу убедиться, что безопасно предположить, что GetMyObject () никогда не вернет ноль.

Ну, хорошо, если это все, что тебя волнует. Но давайте проясним кое о чем. Ваш метод НЕ является потокобезопасным. Вполне возможно, что будут созданы два экземпляра MyObject, и два разных вызывающих абонента могут в конечном итоге увидеть разные возвращаемые значения, даже если ясно, что вы намерены иметь только один. Чтобы это исправить, я рекомендую просто использовать Lazy<T>:

private static Lazy<MyObject> obj;

static Foo() {
    obj = new Lazy<MyObject>(
        () => new MyObject(),
        true
    );
}

public static MyObject GetMyObject() {
    return obj.Value;
}

public static void ClearMyObject() {
    obj = new Lazy<MyObject>(
        () => new MyObject(), 
        true
    );
}
1 голос
/ 06 октября 2011

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

Используйте вместо этого Lazy<T>, который обеспечит то, что вы ищете.

public static readonly Lazy<MyObject> myObject = new Lazy<MyObject>(() => new MyObject(), true);

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