Lazy singleton в многопоточном приложении С#

Я работаю над многопоточным приложением С#, которое использует веб-службу WCF. Соединение с webservice будет иметь определенный тайм-аут, который мы можем определить, после чего он будет закрыт. Я ищу, чтобы сохранить подключение к веб-службе, используя singleton class. Я пытаюсь получить экземпляр следующим образом:

CLazySingleton ins = CLazySingleton.Instance;
string connection = CLazySingleton.abc;

Ниже приведен код для одноэлементного класса:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LazySingleton
{
    public class CLazySingleton
    {
        private static readonly Lazy<CLazySingleton> _instance
            = new Lazy<CLazySingleton>(() => new CLazySingleton());
        private static readonly object ThreadLock = new object();
        public static string abc;  
        //I will use the service connection object in place of 'abc' in the application
        //assume that 'abc' is storing the connection object    

        private CLazySingleton()
        { }

        public static CLazySingleton Instance
        {
            get
            {   
                if (abc == null)
                {
                    lock (ThreadLock)
                    {
                        //Make the connection
                        abc = "Connection stored in this variable";
                        Console.WriteLine("Connection Made successfully");
                        return _instance.Value;
                    }                    
                }
                else
                {
                    return _instance.Value;
                }
            }
        }
    }
}

Мои вопросы: 1. Может ли этот код ухаживать за несколькими потоками, пытаясь получить экземпляр одновременно? В настоящее время моя самая большая проблема. 2. Могу ли я получить лучшее решение для этого? 3. Нужно ли здесь использовать "блокировку" или использовать "ленивый" подход, заботясь о многопоточности, пытающихся получить экземпляр?

Любая помощь будет оценена.

Спасибо!

Ответ 1

В соответствии с документацией Microsoft Lazy Initialization в разделе под названием "Инициализация потоков":

По умолчанию объекты Lazy являются потокобезопасными.

С учетом этого поле abc не должно быть статическим. Поскольку вы используете Lazy<T> для создания экземпляра singleton, вам безопасно инициализировать ваше соединение в конструкторе CLazySingleton.

Ответ 2

Простое использование ThreadSafetyMode

 Lazy<MergeSort> ty = new Lazy<MergeSort>(LazyThreadSafetyMode.ExecutionAndPublication);

Ответ 3

Будет ли этот код иметь возможность следить за несколькими потоками, пытаясь получить экземпляр одновременно?

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

Могу ли я найти лучшее решение для этого?

Да, вы можете. Позвольте мне описать это в последнем блоке этого ответа.

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

Создание свойства Value в классе Lazy является потокобезопасным. В вашем сценарии я бы использовал преимущество свойства IsValueCreated of Lazy < > class. Вам также понадобится объект ThreadLock. Еще одна вещь заключается в том, что после того, как вы получите доступ к свойству Value класса Lazy < > , свойство IsValueCreated вернет true (трюк;-))

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LazySingleton
{
    public class CLazySingleton
    {
        private static readonly Lazy<CLazySingleton> _instance
            = new Lazy<CLazySingleton>(() => new CLazySingleton());
        private static readonly object ThreadLock = new object();
        public static string abc;  
        //I will use the service connection object in place of 'abc' in the application
        //assume that 'abc' is storing the connection object    

        private CLazySingleton()
        { }

        public static CLazySingleton Instance
        {
            get
            {   
                if (_instance.IsValueCreated)
                {
                    return _instance.Value;
                }
                lock (ThreadLock)
                {
                    if (abc == null)
                    {
                        abc = "Connection stored in this variable";
                        Console.WriteLine("Connection Made successfully");
                    }
                }
                return _instance.Value;
            }
        }
    }
}