2014-07-09 1 views
0

잠금 (예 : 중요 섹션) 내부에서 발생하는 이벤트는 교착 상태에 빠지기 쉽습니다. 이는 이벤트 처리기가 일부 비동기 작업에서 차단해야 할 수도 있기 때문입니다 같은 자물쇠를 사십시오..NET 이벤트 발사 및 처리에 대한 모범 사례

가 잠금 내부 이벤트를 발생하기 위해 필요한 경우
  1. 후 항상 비동기 이벤트를 발생 : 자, 디자인 현명한 내 마음에 와서 두 가지 가능한 솔루션이 있습니다. 예를 들어, 이벤트 발사 순서가 중요하지 않은 경우 ThreadPool을 사용하여이 작업을 수행 할 수 있습니다. 이벤트의 순서를 보존해야하는 경우 단일 이벤트 실행 스레드를 사용하여 순서대로 이벤트를 시작하지만 비동기 적으로 시작할 수 있습니다.

  2. 이벤트를 발생시키는 클래스/라이브러리는 교착 상태를 방지하고 잠금 내에서 이벤트를 시작하기 위해 필요한 예방 조치를 취할 필요가 없습니다. 이 경우 이벤트 핸들러 내부에서 잠금 (또는 다른 차단 조작)을 수행하는 경우 비동기 적으로 이벤트를 처리하는 것은 이벤트 핸들러의 책임입니다. 이벤트 처리기가이 규칙을 따르지 않으면 교착 상태가 발생할 경우에 대비해야합니다.

솔직히 두 번째 옵션은 이벤트 처리기 또는하지 않을 수 있습니다 무엇을 추측해야하는 이벤트 발사 코드와 같은 원리의 분리 측면에서 더 낫다고 생각합니다.

그러나 실제적으로 두 번째 옵션은 모든 이벤트 처리기가 모든 이벤트 처리 코드를 비동기식으로 실행해야하는 시점으로 수렴되는 것 같으므로 첫 번째 경로를 택할 것입니다. 대부분의 경우에는 일부 일련의 호출이 차단 작업을 수행하는지 여부를 결정합니다. 복잡한 이벤트 처리기의 경우 가능한 모든 경로를 추적하고 코드가 전개됨에 따라 경로를 추적하는 것은 쉬운 일이 아닙니다. 따라서 문제를 한 곳에서 해결하는 것이 바람직합니다.

간과 할 수있는 다른 대체 솔루션이 있는지, 그리고 가능한 장점/단점과 함정이 각각의 가능한 해결책에 기인하는지 보는 데 관심이 있습니다.

이런 상황에서 가장 좋은 방법이 있습니까?

답변

2

세 번째 옵션이 있습니다. 잠금을 해제 할 때까지 이벤트를 발생시키는 지연입니다. 일반적으로 잠금은 잠시 동안 수행됩니다. 일반적으로 잠금 후까지 이벤트 발생을 지연시킬 수 있습니다 (단, 동일한 스레드에서).

BCL은 잠금 상태에서 사용자 코드를 호출하지 않습니다. 이것은 그들의 명시적인 설계 원칙입니다. 예를 들어 ConcurrentDictionary.AddOrUpdate이 아니며은 자물쇠로 잠겨있는 동안 공장에 전화합니다. 이 반 직관적 인 동작은 같은 키에 대해 여러 팩터 리 호출을 유발할 수 있으므로 많은 스택 오버플로 질문을 발생시킵니다.

+0

나는 또 다른 가능한 솔루션이라고 확신합니다. 이벤트를 발생시키는 메서드가 이벤트를 발생시키는 메서드를 잠그고 있는지 여부를 간단하게 확인할 수는 있지만 이벤트 호출 메서드로 이어지는 호출 체인에서 호출 메서드 중 하나가 호출되는지 여부를 확인하는 것은 일반적으로 매우 어렵거나 불가능합니다. 이미 자물쇠가 있습니다. 이러한 경우 잠금 컨텍스트 외부에서 이벤트를 시작하는 것은 매우 어렵습니다. BCL은 관련 예제이지만 BCL은 독립적이고 (일반적으로 작고 스레드 안전하지 않은) 클래스의 모음이기 때문에 복잡한 비즈니스 로직이있는 응용 프로그램 코드에 적용 할 수 있는지 확실하지 않습니다. – exc

+1

@exc 맞습니다 : 코드가 잠긴 영역에서 잠금 및 딥 콜 스택을 많이 사용하는 경우 큰 문제입니다. 그런 정교한 스레딩 코드가 정말로 최고의 솔루션인지 재평가해야한다고 제안하고 싶습니다. 특히 교착 상태가 수동 조작없이 앱이 절대로 회복되지 않는다는 가장 치명적인 오류라고 생각하면됩니다. 귀하의 경우에는 콜백 호출을 스레드 풀로 푸시하는 것이 안전하고 쉽습니다. 이것은 나의 새로운 대답이다. – usr

+0

답변을 제출해 주셔서 진심으로 감사 드리며 원칙적으로 귀하와 동의한다는 사실을 지적하고 싶습니다. 관리하기 어려운 코드를 작성하지 않으려 고 노력합니다. 그러나 이러한 이상적인 접근 방식은 대용량 코드베이스 (수백만 줄의 코드)가있는 경우 거의 적용 할 수 없다는 점을 명심하십시오. 대부분의 코드는 수십 명의 사람들이 작성했으며 현재 최소한 15 명으로 이루어진 그룹. 나는 점진적으로 코드를 개선하려고 노력하고 있으며, 따라서 내 질문을한다. – exc

0

솔직히 이벤트 발사 코드는 이벤트 처리기가 수행할지 여부를 추측해서는 안되기 때문에 두 번째 옵션은 관심 원칙의 분리 측면에서 더 좋습니다.

첫 번째 옵션이 우려 사항을 위반하지 않는다고 생각합니다. (분명 완료되지 않음) 같은 것을 AsyncEventNotifier 클래스를 추출하고 여기에 이벤트 발생 객체의 대리자를 가지고 :

class AsyncEventNotifier 
{ 
    private List<EventListener> _listeners; 

    public void AddEventListener(EventListener listener) { _listeners.add(listener); } 
    public void NotifyListeners(EventArgs args) 
    { 
     // spawn a new thread to call the listener methods 
    } 
    .... 
} 

class EventGeneratingClass 
{ 
    private AsyncEventHandler _asyncEventHandler; 

    public void AddEventListener(EventListener listener) { _asyncEventHandler.AddEventListener(listener); } 

    private void FireSomeEvent() 
    { 
     var eventArgs = new EventArgs(); 
     ... 
     _asyncEventhandler.NotifyListeners(eventArgs); 
    } 
} 

은 이제 원래의 클래스가 이전에 대해 책임을지지 않습니다이었다 아무것도에 대한 책임을지지 않습니다.리스너를 자신에게 추가하는 방법을 알고 있으며 리스너에게 이벤트가 발생했음을 알리는 방법을 알고 있습니다. 새로운 클래스는 "청취자에게 이벤트가 발생했음을 알립니다"라는 구현 세부 사항을 알고 있습니다. 리스너 순서는 중요한 경우 보존됩니다. 아마도 그렇게해서는 안됩니다. listenerA가 이미 처리 할 때까지 listenerB가 이벤트를 처리 할 수없는 경우 listenerB는 수신 대기중인 객체보다 listenerA가 생성 한 이벤트에 더 많은 관심을가집니다. 이벤트가 처리되어야하는 순서를 알아야하는 또 다른 클래스가 있어야합니다.

+0

답변을 주셔서 감사 드리며 미안하지만 충분히 명확하지 않은 경우 이것이 정확히 첫 번째 옵션에서 설명하려고했던 것입니다. SoC에 언급 한 이유는 코드가 먼저 비동기 적으로 이벤트를 발생시키는 이유가 있어야한다는 것입니다. 비동기 적으로 이벤트를 발생시키는 이유는 무엇입니까? 핸들러가 막힐 수 있기 때문입니다. 이 발사 방법이 정말로 이것을 고려해야합니까? 이것이 바로 SoC에 대한 이유입니다. 그런데 "이벤트 순서 처리"에서는 다른 핸들러가 단일 이벤트를 처리하는 순서가 아니라 서로 다른 이벤트를 실행하는 순서를 의미했습니다. – exc