Каковы опасности не-потокобезопасного синглтона? - PullRequest
1 голос
/ 22 июня 2019

Недавно я нашел эту статью , в которой объясняется, как реализовать thread safe singleton pattern в C #.Однако мне интересно, насколько важно сделать ваш синглтон-поток безопасным, что и как опасно, если ваш синглтон-класс не потокобезопасен.Рассмотрим очень простую, не поточно-ориентированную реализацию одноэлементного шаблона:

public class ThreadNotSafeSingleton
{
    public static ThreadNotSafeSingleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new ThreadNotSafeSingleton();
            }

            return _instance;
        }
    }

    private static ThreadNotSafeSingleton _instance;

    private ThreadNotSafeSingleton()
    {
    }
}

Теперь, скажем, из-за того, что этот код не является потокобезопасным, два потока будут вводить эту строку кода _instance = new ThreadNotSafeSingleton();.В этом случае первый поток инициализирует поле _instance, а затем второй снова инициализирует поле _instance, в любом случае в результате в вашем приложении будет существовать один _instance.

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

Ответы [ 2 ]

3 голосов
/ 22 июня 2019

Цель шаблона Singleton - создать только один и только один объект из типа.

Представьте себе thread1 и thread2 для доступа к элементу доступа get свойства Instance в первый раз (_instance по-прежнему null) * одновременно 1011 * :

thread1 проверяет выражение if (_instance == null) и видит, что _instance равно null и одновременно , thread2 проверяет выражение if и видит, что _instance равно null.thread1 приходит к этому выражению _instance = new ThreadNotSafeSingleton(); и создает новый объект типа ThreadNotSafeSingleton.Поскольку thread2 проверяет выражение if и видит, что _instance равно null, он попадает в блок кода if и создает новый объект, который будет перезаписывать ссылку на переменную _instance.Наконец, thread1 использует объект, который отличается от объекта thread2.

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

3 голосов
/ 22 июня 2019

Поскольку шаблон синглтона ограничивает класс только одним экземпляром, ваш подход нарушает этот шаблон.

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

Ключевое слово volatile часто упоминается как возможное решение, но оно не решит проблему синхронизации (как указано BionicCode), когда поток 1 проходит линию if (_instance == null) и переводится в спящий режим, поток 2 также оценивает то же самое, если и получает возможность создания синглтона.Когда поток 1 просыпается позже, он создает другой синглтон.

...