2016-09-23 3 views
3

비동기 코드를 사용하여 웹 서비스 및 기타 응용 프로그램 서버에 연결하는 TopShelf 서비스가 있습니다.TopShelf에서 비동기 Start() 오류를 처리하는 방법

시작할 때 연결을 초기화 할 수없는 경우 서비스에서 일부 오류를 기록하고 올바르게 중지해야합니다.

시작 조건이 부합하지 않을 때 TopShelf를 중지하는 것에 대해 this question을 보았습니다. This answer은 TopShelf HostControl을 사용하여 서비스를 중지하는 방법에 대해 설명합니다.

그러나 그 대답은 ServiceConfigurator<T>.WhenStarted<T>(Func<T, HostControl, bool> start) 방법에 의존합니다.

나는 현재 표준 방법으로 TopShelf 서비스를 구성하고있다 :

x.Service<MyService>(s => 
{ 
    s.ConstructUsing(() => new MyService()); 
    s.WhenStarted(s => s.Start()); 
    s.WhenStopped(s => s.Stop()); 
}); 

내 서비스의 Start() 방법은 다음과 같이 정의, 실제로 async이다 그러나 :

public async void Start() 
{ 
    await Init(); 
    while (!_canceller.Token.IsCancellationRequested) 
    { 
     await Poll(); 
    } 
} 

이 잘 작동하는 것 같다. 하지만 함수의 여러 위치에서 await 키워드를 사용합니다. 따라서 Start() 메서드를 HostControl으로 바꾸고 bool을 반환하는 것은 을 async 메서드에서 반환해야하기 때문에 변경할 수 없습니다.

현재 예외 (Top)가 표시되어 예외가 터지면 자동으로 서비스를 중지 할 수 있도록 Start() 함수에서 예외가 발생합니다. 그러나 예외는 내 코드에 완전히 처리되지 않으므로 필자가 작성한 다양한 로그에 처리되지 않은 예외 오류 메시지가 표시됩니다. 좋은 오류 메시지와 깨끗한 서비스 종료로 대체하는 것을 선호합니다.

  1. TopShelf에 대한 async void Start() 방법을 사용하여 어떤 문제가 있습니까 :

    그래서, 나는이 개 질문이?

  2. Init()이 예외를 throw하는 경우 내 서비스가 async 코드를 실행한다고 가정하면 예외 세부 정보가 정상적으로 기록되고 서비스가 중단되도록하는 방법이 있습니까?

답변

5

첫째로, async void은 실제로 일부 화재 및 잊어 버림 시나리오를 제외하고는 거의 항상 올바르지 않습니다. 이 값을 async Task으로 변경하고 싶습니다.

때때로 동기화 및 비동기 코드 경계에서 .Wait()을 사용해야합니다. 이 경우 당신은 아마 StartAsync()에 현재 비동기 Start() 방법을 이름을 변경하고 그것을 호출하는 Start() 방법을 추가 할 : 그러나

public void Start() 
{ 
    StartAsync().Wait(); 
} 

public async Task StartAsync() 
{ 
    await Init(); 
    while (!_canceller.Token.IsCancellationRequested) 
    { 
     await Poll(); 
    } 
} 

그 TopShelf의 Start() 방법은 "Run"() 방법이 아닙니다, 당신은 또 다른 문제가; 즉 서비스가 시작되는 즉시 해당 메소드에서 복귀해야하며 서비스가 실행되는 동안 거기에 남아 있지 않아야합니다.이미, 아마 대신 Start()에서 Wait()를 호출하지만, TaskStartAsync()에서 반환 저장하지 않는 게 좋을 비동기 await를를 사용하는 감안할 때, 다음 Stop()가 호출 될 때, 오직 그 다음에 _canceller기존의 것을 사용을 중지하기 위해 Task 신호 이 같은 당신을 떠나 Stop() 전화 .Wait()는 :

private Task _serviceTask; 

public void Start() 
{ 
    Init().Wait(); 
    _serviceTask = ExecuteAsync(); 
} 

public void Stop() 
{ 
    _canceller.Cancel(); 
    _serviceTask.Wait(); 
} 

public async Task ExecuteAsync() 
{ 
    while (!_canceller.Token.IsCancellationRequested) 
    { 
     await Poll(); 
    } 
} 

내가 추가해야합니다 당신이 그것을 한 방법 의미에서, 어느 정도 일을 도망, 당신은 아마 종류-의 당신의 비동기 Start() 방법은 것 첫 번째 await에 도착하자마자 TopShelf로 돌아 가야합니다. 계속 실행합니다. Stop() 메서드가 _canceller.Cancel()을 호출하면 비동기 Start() 메서드는 다음 번에 Poll()이 호출되면 종료됩니다.

그러나 위는 더 깨끗하고 이전에 실행하지 않은 마지막 Poll()이 끝날 때까지 기다려야합니다. 또한 예외를 처리 할 수 ​​있습니다.

편집 는 또한 위와 같이, Start()Init() 전화를 이동할 것입니다.

+0

'ExecuteAsync()'에 의해 반환 된'_serviceTask'가'Start()'메소드에서 기다리지 않고 실제로 시작하는지 어떻게 확인합니까? 'Start()'가 비동기 일 필요가 있기 때문에 기다릴 수 없습니다. 'Task.Delay()'를 기반으로 한 모의'Poll()'메소드를 가진 테스트 코드에서'_serviceTask.Start()'를 호출하면'InvalidOperationException' 메시지가'Start promise-style task.' 만약 내가 작업을 기다리지 않거나 시작하지 않는다면 WaitingForActivation 상태로 남아있는 것 같습니까? – Hydrargyrum

+0

신경 쓰지 마라, 나의 테스트 코드가 빨렸다. ExecuteAsync()는 실행중인 작업을 반환하므로 시작하기 위해 특별한 작업을 수행 할 필요가 없습니다. 나는 왜 태스크의 상태가'WaitingForActivation'인지는 모르겠지만 태스크는 실제로 실행되고 결국 완료된다. – Hydrargyrum

+0

ExecuteAsync가 예외를 throw하면이 문제가 하나도 발견되지 않습니다. – Peter