2016-09-23 32 views
3

Webサービスや他のアプリケーションサーバーに接続するために非同期コードを使用する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メソッドから返す必要があるため、単純に変更することはできません。

私は現在、例外が起きたときにTopShelfがそれらを見て、自動的にサービスを停止できるように、Start()関数からバブルアップすることを許可しています。しかし、例外は私のコードでは完全に処理されていないので、私が書き込むさまざまなログに、扱いにくい例外エラーメッセージが残ることになります。どちらが良いエラーメッセージとクリーンなサービスシャットダウンで置き換えることを好むでしょう。

  1. TopShelfためasync void Start()方法を使用してに問題がある:

    だから、私は2つの質問がありますか?

  2. Init()が例外をスローした場合、例外サービスの詳細は正常に記録され、サービスは停止します。私のサービスでは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"()方法ではありませんで、あなたは、別の問題があります。つまり、サービスが開始されるとすぐにそのメソッドから戻り、サービスが実行されている間はそこにとどまることはありません。あなたはすでに非同期のawaitを使っている考えると、私はおそらく代わりにStart()Wait()を呼び出しますが、Taskを保存していないと思いますStartAsync()から返さStop()が呼び出されたときに、その後、中だけにして_canceller既存、およびことを使用して停止するように、あなたのTaskを知らせますStop()コール.Wait()、このような何かをあなたに残して:あなたの非同期Start()方法がすることを私は意味では、あなたはおそらく種類-の逃げる事限り、あなたはそれを持っていた道にあることを追加する必要があります

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(); 
    } 
} 

最初のawaitに当たったら直ちにトップシェルフに戻りますが、私は実行を続行します。 Stop()メソッドが_canceller.Cancel()を呼び出す場合、次回のPoll()が呼び出されると、非同期Start()メソッドが終了します。

しかし、上記はより洗練されており、前回のPoll()の実行が終了するまで待つことができなくてはなりませんでした。また、例外を処理することもできます。

編集 私はまた、上記のように、Start()Init()コールを移動すると思います。

+0

'ExecuteAsync()'によって返された '_serviceTask'が' Start() 'メソッドで待たずに実際に起動することを確認するにはどうしたらいいですか? 'Start()'が非同期でなければならないので、私はそこで待つことができません。 'Task.Delay()'をベースにした模擬 'Poll()'メソッドを使ったテストコードでは、 '_serviceTask.Start()'を呼び出すと、 'InvalidOperationException'というメッセージがスローされます。私がタスクを待っていない、または開始していなければ、WaitingForActivation状態にとどまっているようです。 – Hydrargyrum

+0

気にしないでください、私のテストコードが吸い込まれました。 ExecuteAsync()は実行中のタスクを返しますので、起動するために特別な操作を行う必要はありません。タスクのステータスが「WaitingForActivation」ではなく実行中である理由が分からないが、タスクが実際に実行されて最終的に完了する。 – Hydrargyrum

+0

ExecuteAsyncが例外をスローした場合、これは決して気付かれません。 – Peter

関連する問題