2009-11-12 5 views
10

배경 : 스레드로부터 안전하게 만든 피사체/관찰자 디자인 패턴을 구현하는 클래스가 있습니다. subjectobservers이라는 알림을 알리는 메시지와 동일한 스레드에서 observer이 생성 된 경우 간단한 메서드 호출 observer->Notified(this)으로 알립니다. 그러나 observer이 다른 스레드에서 생성 된 경우 queue에 알림이 게시되어 나중에 observer을 구성한 스레드에서 처리 된 다음 알림 이벤트가 처리 될 때 간단한 메서드 호출을 수행 할 수 있습니다.싱글 톤을 제거하는 데 도움이됩니다 : 대안을 찾으십시오

그래서 ... 스레드와 대기열이 생성되고 삭제 될 때 업데이트되는 스레드와 대기열을 매핑하는지도가 있습니다. 이 맵 자체는 뮤텍스를 사용하여 멀티 스레드 액세스를 보호합니다.

지도는 싱글 톤입니다.

나는 "이 응용 프로그램에 하나만있을 것"이기 때문에 과거에 싱글 톤 (singleton)을 사용한 것에 대해 유죄를 지켰으며, 저를 믿습니다 - 나는 내 보속을 지불했습니다!

응용 프로그램에서 실제로 대기열/스레드 맵이 하나만 존재할 것이라고 생각하는 한 부분은 도움이되지 않습니다. 다른 목소리는 싱글 톤이 좋지 않아서 그들을 피해야한다고 말합니다.

나는 싱글 톤을 제거하고 내 유닛 테스트를 위해 스텁 할 수 있다는 생각을 좋아합니다. 문제는 좋은 대안 솔루션을 생각하기가 힘듭니다.

과거에 성공한 "일반적인"솔루션은 단독 개체를 참조하는 대신 사용할 개체에 대한 포인터를 전달하는 것입니다. 관측자와 피사체는 내 응용 프로그램에서 10 페니이기 때문에이 경우 까다로울 것이라고 생각합니다. 대기열/스레드 맵 객체를 모든 단일 관찰자의 생성자로 전달해야하는 것은 매우 어색합니다.

감사합니다. 내 응용 프로그램에는지도가 하나 뿐이지 만 의사 결정 클래스가있는 곳에서는 관찰자 클래스 코드에 있지 않아야합니다.

어쩌면 이것은 유효한 싱글 톤이지만, 제거 방법에 대한 아이디어도 있습니다.

감사합니다.

추신. 수락 대답에 언급 된 What's Alternative to Singletonthis article을 읽었습니다. 나는 ApplicationFactory가 다른 이름으로 또 하나의 싱글 톤이라고 생각하는 것을 도울 수 없다. 나는 정말로 이점을 보지 못한다.

+2

왜 싱글 톤을 피 하시겠습니까? 그들은 그들의 자리를 가장 분명히 가지고 있습니다. 모든 관용구는 오용되고 남용 될 수 있습니다. 그러나 thread-> notification_queue의 애플리케이션 전반에 걸친 맵은 나에게 합당한 것처럼 보인다. – Mordachai

+0

@Mordachai : 싱글 톤이 자리를 잡았다는 것을 알고 있으며이 대기열/스레드 맵이 완벽하게 유효 할 수도 있습니다. 몇 가지 단위 테스트를 쓰고있을 때 버그가 나기 시작했고 거기에 싱글 톤을 사용하는 것이 어색했다. –

+0

어떤 스레드 라이브러리를 사용하고 있습니까? – outis

답변

0

옵저버는 값이 비싸지 만 notification-queue-thread 맵에 의존합니다. 맞습니까?

종속성을 명시 적으로 지정하고 제어하는 ​​것에 대해 어색한 점이 있습니까?

응용 프로그램 팩토리 Miško Hevery는 자신의 기사에서 가장 큰 장점은 1) 공장 접근 방식이 종속성을 숨기지 않고 2) 의존하는 단일 인스턴스가 전역 적으로 사용할 수 없으므로 다른 개체 그들의 상태에 간섭 할 수 있습니다. 따라서이 접근 방식을 사용하면 특정 최상위 수준의 응용 프로그램 컨텍스트에서지도를 사용하는 대상을 정확히 알 수 있습니다. 전역 적으로 액세스 할 수있는 싱글 톤을 사용하면 어떤 클래스를 사용하더라도 맵과 함께 또는 맵에 불미스러운 점이있을 수 있습니다. 해결책 대한

+0

관찰자의 사용자는 대기열/스레드 메커니즘이 투명하다는 의도가 있습니다. 파생 된 관찰자는 스레드가 관련된 경우 Notified() 메소드가 반드시 동기 메소드 호출이 아니라는 점을 알아야합니다. 옵저버 기본 클래스는지도와 알림 대기열에 따라 달라질 수 있지만 종속성을 파생 클래스로 드래그하여 각 파생 된 관찰자가 대기열/스레드 맵에 대해 알도록하고 싶지 않습니다. 내 앱에는 많은 관찰자가 (> 500) 있습니다! (나는 그것이 당신이 "통제권을 가진"것이 무엇을 의미한다고 생각합니다.) –

+0

사실, 그것은 내가 의미했던 것입니다! 당신이 인내심을 잃지 않거나 합창단을 설교하는 것은 아닙니다. 싱글 톤 (singleton)과 비슷하지만 싱글 톤 (singleton)의 투명도는 단지 환상적입니다. 즉, 그것은 실패 할 때까지만 투명합니다. 아마도 JS Bangs의 아이디어는 500 개의 파생 된 관찰자 클래스 (yikes)를 가지고 있다면 더 좋을 것입니다 –

+0

@ Jeff : 피사체 당 500 명이 넘는 옵저버는 아닙니다 :-) 많은 관측자와 많은 피사체가 다양한 정도의 다 대다 관계로 있습니다 –

1

일부의 생각 :

왜 다른 스레드에서 생성 된 관찰자에 대한 알림을 대기열에해야합니까?필자가 선호하는 디자인은 subject이 관찰자에게 직접 알림을 보내고 다른 스레드에서 언제든지 Notified()이 호출 될 수 있다는 사실을 알고 관찰자에게 안전하게 스레드를 구현하도록하는 것입니다. 참관인은 자국의 어느 부분이 자물쇠로 보호되어야하는지 알고 있으며, subject 또는 queue보다 더 잘 처리 할 수 ​​있습니다.

실제로 queue을 유지해야 할 충분한 이유가 있다고 가정하고 인스턴스로 만들지 않겠습니까? 어딘가에 main을 입력하고 해당 참조를 전달하십시오. 모든 것이 하나만있을 수 있지만, 당신은 여전히이를 인스턴스가 아닌 전역 정적이 아닌 것으로 처리 할 수 ​​있습니다.

+0

개인적으로, 다중 스레드 관찰자를 작성할 때, 필자는 관찰자 메소드를 항상 자신의 스레드의 컨텍스트에서 호출하는 것을 선호합니다. obeservation 활동이 매우 매우 가벼운 경우에만 유용합니다. – Mordachai

+0

@JS Bangs : thread-safety에 관해서 당신이 말하는 것을 볼 수 있습니다, 그러나 현재의 구현은 주체가 관찰자가 아닌 뮤텍스와 스레드 안전성을 다루도록하는 것입니다. 이제 팀을 바꾸는 것이 팀과 잘 어울리지 않을 것입니다! (덧붙여 말하자면, 그것은 큐가 아닌 싱글 톤인 큐 - 쓰레드 맵입니다. 큐와 쓰레드가 많으며 하나의 맵이 있습니다. 모든 참조 개체 인스턴스에 대해이 참조를지도 개체에 전달해야하는 데 필요한 변경 사항이 무엇인지 걱정 스럽습니다. –

+0

@Mordacahai : 예, 이것이 제가 취한 접근법입니다. 각 관찰자의 Notified() 메소드는 소유 스레드의 컨텍스트에서 실행됩니다. 비동기 통지 (대기열) (단일 스레드의 경우) 또는 강제 통지 (메소드 호출) (다중 스레드의 경우)를 강제하는 메커니즘이 있지만 사용되는 경우 거의 사용되지 않고 '고급'사용을위한 메커니즘이 있습니다 그 결과를 알고 있어야합니다. –

1

대기열을 대상 클래스에 넣는 것은 무슨 문제입니까? 지도가 필요한게 무엇입니까?

이미 싱글 톤 큐 맵에서 읽는 스레드가 있습니다. 대신 단순히 관찰자 구독 할 대상 클래스 내부지도를하고 두 가지 방법을 제공하는 일을 :

class Subject 
{ 
    // Assume is threadsafe and all 
    private QueueMap queue; 
    void Subscribe(NotifyCallback, ThreadId) 
    { 
    // If it was created from another thread add to the map 
    if (ThreadId != This.ThreadId) 
     queue[ThreadId].Add(NotifyCallback); 
    } 

    public NotifyCallBack GetNext() 
    { 
    return queue[CallerThread.Id].Pop; 
    } 
} 

을 지금 ... 파견 시작 GETNEXT 메소드를 호출 할 수있는 모든 스레드 물론이 모든 지나치게 단순화 그러나 그것은 단지 아이디어입니다.

참고 : 나는 이미이 모델 주위에 아키텍처가 있으므로 이미 많은 수의 관찰자, 하나 이상의 주제가 있고 스레드가 이미지도로 이동한다는 가정하에 작업하고 있습니다. 알림. 이것은 싱글 톤을 제거하지만 같은 스레드에서 알림을 보내고 관찰자가 동시성 문제를 처리하도록 제안합니다.

+0

스레드 당 하나의 대기열이 있습니다. 하나의 스레드에서 많은 관찰자가 구성 될 수 있습니다. 또한 많은 주제가 동일한 스레드에서 통지 할 수 있으므로 주제 당 하나의 대기 행렬이 지나치게 많습니다. –

0

내 접근 방식은 관찰자가 주제에 등록 할 때 대기열을 제공하도록하는 것이 었습니다. 관찰자의 소유주는 쓰레드와 연관된 큐 모두에 책임이 있으며, 피험자는 관찰자를 큐와 연관시킬 것이다. 중앙 레지스트리를 필요로하지 않는다.

스레드 안전 관찰자는 대기열없이 등록 할 수 있으며 제목에 의해 직접 호출 될 수 있습니다.

+0

무슨 뜻인지 알지만, 너무 많은 의존성을 드러내는 것 같아요. 파생 된 옵저버 및 주체로부터 큐 동작을 숨기는 이점 중 하나는 스레드 간 (소스 코드에서 런타임이 아닌) 관찰자를 이동하고 사용할 큐를 '자동으로'알 수있게하는 것입니다. 관찰자와 피험자의 사용자는 구현 세부 사항에 대해 걱정할 필요가 없습니다. –

+0

이 경우, 싱글 톤은 아마도 당신이 원하는 것일 것입니다. 정의 상으로는 프로세스의 모든 스레드 세트가 하나뿐이므로 개념 상 문제는 없습니다. 또는 (덜 이식 가능하지만) 플랫폼에 스레드가있는 경우 스레드 로컬 저장소에 큐를 넣을 수 있습니다. –

3

싱글 톤을 제거하려고하는 유일한 목적이 단위 테스트 관점에서 나온 것이라면, 싱글 톤 게터를 스텁에서 스왑 할 수있는 것으로 바꾸는 것이 좋습니다.

class QueueThreadMapBase 
{ 
    //virtual functions 
}; 

class QeueueThreadMap : public QueueThreadMapBase 
{ 
    //your real implementation 
}; 

class QeueueThreadMapTestStub : public QueueThreadMapBase 
{ 
    //your test implementation 
}; 

static QueueThreadMapBase* pGlobalInstance = new QeueueThreadMap; 

QueueThreadMapBase* getInstance() 
{ 
    return pGlobalInstance; 
} 

void setInstance(QueueThreadMapBase* pNew) 
{ 
    pGlobalInstance = pNew 
} 

그런 다음 테스트에서 대기열/스레드 맵 구현을 스왑 아웃합니다. 적어도 이것은 싱글 톤을 조금 더 노출시킵니다.

+0

아 ... 흥미 롭습니다. 사실, 이미 테스트를 위해이 방식으로 다른 싱글 톤을 나눠서 기본 부분을 로컬에서 재사용하고 싱글 톤 부분을 무시할 수 있지만'setInstance' 메소드를 추가하지 않았습니다. 감사. –

0

테스트간에 호출 할 수있는 초기 상태로 싱글 톤을 반환하는 Reset 메서드를 추가하는 것은 어떻습니까? 그것은 스텁 (stub)보다 간단 할 수도 있습니다. Singleton 템플릿에 일반 Reset 메서드를 추가 할 수 있습니다 (내부 싱글 톤 초기화를 삭제하고 포인터를 다시 설정). 이것은 모든 싱글 톤의 레지스트리를 리셋하는 마스터 ResetAll 메소드를 포함 할 수도 있습니다!

+0

그럴 가치가 있습니다. Snazzer의 "플러그인"싱글 톤 아이디어와 결합 할 수 있습니다. 건배. –

관련 문제