저는 비동기 프로그래밍에 대한 전문가가 아니므로 문제가 있는지 확인하고 싶습니다.이 종속성은 Windsor 싱글 톤 스레드로부터 안전합니까?
캐슬 윈저를 사용하지만 특정 ASP.NET 기능에 내장 된 HttpConfiguration.Services
파이프 라인을 사용하는 웹 API 앱이 있습니다. 이 경우 전역 예외 처리기를 등록합니다. 여기에 코드입니다 : 처리되지 않은 예외가 발생하면
protected void Application_Start()
{
//ASP.NET registers this instance in a ConcurrentDictionary and treats it as a singleton
config.Services.Replace(typeof(IExceptionHandler), container.Resolve<IExceptionHandler>());
}
public class EmailExceptionHandler : ExceptionHandler
{
private readonly SmtpClient client;
private MailMessage _errorMail;
public EmailSender(SmtpClient client, MailMessage message)
//client automatically resolved with smtp settings pulled from web.config by container. Seems okay to be a singleton here.
//message automatically resolved with properties like To, From populated from web.config.
//The intent here is to keep config access out of this class for testability.
{
_errorSmtpClient = errorSmtpClient;
_errorMail = errorMail;
}
public override void Handle(ExceptionHandlerContext context)
{
// set props on the MailMessage e.g. exception detail
_errorSmtpClient.SendAsync(_errorMail);
// standard post-processing, no dependencies necessary
}
}
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<SmtpClient>().DependsOn(Dependency.OnAppSettingsValue(/*...*/)));
container.Register(Component.For<MailMessage>().Named("errorMailMessage")
.DependsOn(Dependency.OnAppSettingsValue(/*...*/)).LifestyleTransient());
//transient here should bind lifetime to exceptionhandler singleton's lifetime
container.Register(Component.For<IExceptionHandler>().ImplementedBy<EmailExceptionHandler>()
.DependsOn(Dependency.OnComponent("message", "errorMailMessage")));
}
, ASP.NET은 등록 IExceptionHandler
에 대한 자사의 서비스를 사전에 찾아 가서하면 오류 컨텍스트를 전달합니다. 이 경우 Windsor에서 배선하고 응용 프로그램 시작시 등록한 처리기입니다. 결코 존재하지 인해 부모 싱글에
Task IExceptionHandler.HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
if (context == null)
throw new ArgumentNullException("context");
ExceptionContext exceptionContext = context.ExceptionContext;
if (!this.ShouldHandle(context))
return TaskHelpers.Completed();
return this.HandleAsync(context, cancellationToken);
}
public virtual Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
this.Handle(context);
return TaskHelpers.Completed();
}
MailMessage
는 응용 프로그램 시작시 해결 및 컨테이너의 평생 동안 지속되는 : 여기
MailMessage
을 관리하는 코드로 들어가서 바람직하지 않은 상태로 남을 수 있습니다.
복잡성은 동시 스레드가 MailMessage
의 상태를 변경할 수있는 잠재적 인 문제가 있는지 파악할 수있을뿐만 아니라 스레드를 올바르게 처리하여 문제를 해결할 수 있는지 확인해야합니다. 교착 상태는 흐름의 비동기 특성 때문입니다.
이 문제는 내가 그것을 해결하기 위해 여러 가지 방법을 생각할 수 있어야합니다 :
는 메시지의 설정 주위에 잠금 문을 작성하고 이메일로 전송. void 메쏘드 자체가 비동기 적이기 때문에, 유일한 단점은 동시 스레드가 진입 할 때까지 차단하는 것 같습니다. 이 또한
SemaphoreSlim
의Wait()
방법을 사용하는 것과 같지 않습니까?Typed 팩토리 종속성을 만들고 Handle 메서드에서 MailMessage의 인스턴스를 명시 적으로 해결하고이를 로컬 변수에 할당합니다.
async를 끝까지 사용하고
SemaphoreSlim.WaitAsync()
을 호출하여 다른 스레드를 차단하지 마십시오.
:
public override async void Handle(ExceptionHandlerContext context)
{
await _semaphoreSlim.WaitAsync();
try
{
await _errorSmtpClient.SendMailAsync(_errorMail);
}
finally
{
_semaphoreSlim.Release();
}
}
예, 일반적으로 일시적인 종속성이있는 단일 구성 요소가있는 것이 우려의 원인 일 수 있습니다. 외부 구성 요소를 일시적으로 만들 수없는 경우 옵션 2로 이동합니다. 또는 AppSettings에서 여전히 채워질 수있는 자체 Singleton ErrorMailSettings 클래스는 필요하며 내부에서 필요에 따라 MailMessage를 동적으로 만듭니다. 귀하의 Handle() 메소드. –
나는 설정이 응용 프로그램의 수명 동안 변경되지 않기 때문에 메시지가 아닌 설정을 주입하는 아이디어를 좋아합니다. 이것이 내 시나리오의 편리한 해결 방법이지만, 국가와의 의존성을 주입해야하는 상황을 처리하는 방법을 묻는 것은 가치가 있다고 생각합니다. – moarboilerplate
나는 정말로 우려의 영역이 2 개 있다는 사실을 설명하기 위해 약간의 질문을 업데이트했다. – moarboilerplate