C# Singleton 사용에 대해

안녕하세요. :smile:

예전부터 막연하게 Singleton을 사용해왔는데요.
사실 정확하게 이해하고 사용하지 못해 부족함을 많이 느낍니다.

그래서 좀 더 자연스럽고 잘 이해하며 사용하고 싶은 마음에 질문 드립니다!

  • 싱글톤 구현 문법, 구조에 대한 사상
  • 응용 사례, 아이디어
  • 더 알아두면 도움되는 내용들

그리고 저는 Singleton 패턴을 아래와 같은 상황에 사용하고 있는데요.


일반적으로 Instance를 생성하지 않고 단순 기능을 제공하는 클래스

public class EnumWindow
{
    static EnumWindow Instance;

    static EnumWindow()
    {
        Instance = new EnumWindow();
    }

    public List<WinAppInfo> GetPrograms();
}

그리고 public 생성자를 제한하면서 인스턴스를 관리하고 싶을 때

public interface IService
{
    IService Create();
    List<Service> Services { get; }
}

public class Service : IService
{
    private static IService Instance;

    public static IService Get() => Instance;

    static Service()
    {
        Instance = new Service();
    }

    private Service()
    {

    }

    public List<Service> Services { get; private set; }

    public IService Create()
    {
        Service service = new Service();
        Services.Add(service);
        return service;
    }
}

정말 기본으로만 사용하고 있어서요.
여기 계신 분들은 어떤 식으로 활용하고 계신지도 정말 궁금합니다!

읽어주셔서 감사합니다. :smile:

좋아요 3

저는 이렇게 코딩을 합니다.

using System;

Console.WriteLine(Singleton.Instance.Next());
Console.WriteLine(Singleton.Instance.Next());
Console.WriteLine(Singleton.Instance.Next());

public sealed class Singleton
{
    private static Singleton instance;
    private static readonly object @lock = new();

    private int number;

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                lock (@lock)
                {
                    if (instance == null)
                        instance = new Singleton();
                }
            }

            return instance;
        }
    }

    public int Next() => number++;
}

아래의 글은 Lazy<T>를 이용한 방식도 소개하고 있습니다.

스레드에 안전한 Singleton :: 주제없는 윤로그 (tistory.com)

싱글톤 패턴은 인스턴스가 하나일 때 유용할 경우 쓰이면 당연히 좋구요,
싱글톤 인스턴스에서 자유롭게 데이터를 추가하거나 삭제하는 로직이 많아질 경우, 소스코드 관리가 어려워 지므로 지양해야 합니다. 만약 소비해야 할 데이터를 싱글톤 인스턴스에서 가지고 있어야만 한다면, 최초 Instance가 만들어질 때 인자를 이용해 (보통 Options이란 이름을 많이 쓰곤 했습니다) 그 데이터를 확정하는게 좋습니다. 이후 싱글톤 인스턴스의 데이터를 여러 곳에서 소비하는 형태로 말이죠.

Factory 성질의 클래스를 제네릭 싱글톤으로 만들곤 합니다.

좋아요 4

싱글톤은 GoF에도 소개된 패턴이라 많은 사람들이 알고 계시고, 패턴중에 가장 쉽기때문에 많이들 사용하시는 거로 압니다. 그리고 그만큼 호불호 가 분명히 존재하는 패턴이기도 합니다.

저 또한 싱글톤을 그다지 좋아하지 않는데요. 제가 싱글톤을 사용하는 경우는 제가 싱글톤 클래스를 직접 정의하지 않고 IoC 컨테이너를 통해 클래스를 싱글톤 인스턴스로 등록 하는 경우입니다.

싱글톤에 대해 제대로 알기 위해서는 먼저 static 객체와 싱글톤의 차이부터 알아야 하는데요.

static 객체는 CLR이 유일한 인스턴스를 보장 해주는 인스턴스 형태입니다. 싱글톤 역시 인스턴스는 static형태로 가지고 있습니다. 프로세스 내에서 아무데서나 막 접근해도 유일한 인스턴스이기 때문에 개발 시 간편하게 사용할 수 있으나, static 객체에 여러 맴버를 추가해놓고 쉽게 프로그래밍을 한다면, 스파게티 코드 및 static 객체에 대한 의존성이 엄청나게 생겨서 프로그램 유지보수에 좋지 않은 영향 을 끼칩니다. 싱글톤은 나아가, 하나의 클래스기 때문에 static 객체에 대하여 자신만의 추가 기능을 구현하거나, 클래스기 때문에 상속 을 할 수 있다는 장점이 있습니다. 하지만 이렇게 추가 기능을 여러가지를 구현하면 역시 의존도가 높아지기 때문에 프로그래밍 구조를 깨뜨릴 수 있게 될 겁니다.

최초에 싱글톤을 적절한 의존성을 지니고 개발에 대해 편의성을 제공하려고 만들었다가도, 퇴사를 하고 이 프로그램을 잘 모르는 다음 타자가 손 대면서 싱글톤을 자주 이용하면 결과적으로 좋지 못한 프로그램이 되어 갈 것 같다는 제 생각입니다.

하지만 싱글톤에 대해서 좀 자세히 알고 싶다면 아래 포스팅을 참고하시면 좋을 듯합니다. 여러 C#의 싱글톤 코딩 형태에 대하여 VS가 자동으로 해주기 때문에 크게 의미가 없다고 주장하는 글입니다.

https://blog.naver.com/vactorman/220481200251

싱글톤 얘기가 나오면 스레드에 대해 안전하지 않다 는 말이 자주 나옵니다.
일반적으로 스레드에 대해 안전하지 않다. 라는 말은 특정 객체에 대하여 여러 스레드가 데이터를 추가, 수정, 삭제할 때 여러 스레드가 그 객체의 데이터를 조회한 시점에서 스레드들의 객체 조회값이 서로 일치하지 않을 경우 입니다. 예를 들어 List 클래스는 멀티스레드에 대해 안전하지 않은 객체이며, 10개의 스레드가 동시에 접근하여 데이터를 추가하고 삭제하고 조회했을 때 스레드들이 조회한 list의 Item count는 모두 제각각 일 것입니다.

이런 경우 .NET의 동기화 컬렉션을 사용하곤 합니다.

물론 싱글톤 객체에 대하여 여러 스레드가 동시에 접근하는 일이 없다면, 굳이 멀티스레드에 대한 대비를 하지 않으셔도 될 것 같습니다.

좋아요 6

@dimohy 코드 공유해주셔서 정말 감사드립니다. :smile:

저도 이 부분 소스코드에 추가했습니다!

그런데 저는 static 생성자에서 Instance를 생성했는데요.

큰 차이는 없을까요?

@Vincent 상세한 답변 감사합니다.

꼼꼼하게 잘 읽어봤습니다.
스레드는 전혀 생각 안하고 있었어요. :sweat_smile:

참고해주신 글도 잘 읽어보겠습니다!

좋아요 1

네. 상관 없습니다. 다만, 생성 시 인자가 필요없을 때 유용할 것 같아요

좋아요 2