2016-08-12 18 views
5

Self-Hosted OWIN環境内のApiControllerで長時間実行されるタスク(たとえば4-5分)を実行したいとします。しかし、私はそれを完了するのを待つことなく、(長い実行タスクを開始するとすぐに)そのタスクを開始した後に応答を送り返したいと思います。この長時間実行されるタスクは、HTTPとは何の関係もなく、非常に長くかかる可能性があるいくつかのメソッドを順番に実行します。ApiControllerで長時間実行されるタスク(WebAPI、自己ホスト型OWINを使用)

私はthisブログの投稿を見て、QueueBackgroundWorkItemを試してみることにしました。しかし、このメソッドを自己ホスト型(コンソールアプリケーション)のowin環境で使用することが可能か、それを使用する必要があるかどうかはわかりません。自己ホストコンソールアプリケーションでは、アプリケーション自体がリクエストを管理し、すべてのリクエストが同じAppDomain(アプリケーションのデフォルトAppDomain、新しいappDomainを作成しない)で実行されるので、私はちょうど特別なことをしなくても、ファイヤー・アンド・ファット・ファッションを楽しめますか?私はQueueBackgroundWorkItemを使用する場合

とにかく、私は常にエラーを取得:

<Error> 
<Message>An error has occurred.</Message> 
<ExceptionMessage> 
Operation is not valid due to the current state of the object. 
</ExceptionMessage> 
<ExceptionType>System.InvalidOperationException</ExceptionType> 
<StackTrace> 
at System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(Func`2 workItem) at BenchMarkService.SmokeTestController.IsItWorking() in C:\Devel\Code\Projects\BenchMarkService\BenchMarkService\SmokeTestController.cs:line 18 at lambda_method(Closure , Object , Object[]) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext() 
</StackTrace> 
</Error> 

はまた、私はSOにthis質問を発見し、正直に言うと、それはそれを達成するための唯一の方法として、私には少し混乱しそうですIRegisteredObjectですか?

私はちょうど自己実行型アプリケーションで長時間実行されるタスクを実行しようとしています。ほとんどすべてのリソース、私が見つけた質問はasp .netに基づいており、どこから始めるべきかはわかりません。

+0

(データベース内の)タスクをキューに入れ、サービスで処理するオプションはありませんか? –

+0

Hangfire(https://www.hangfire.io/)は私が知る限り、これと似たようなことをしています。しかし、私はこれが質問とは少し違うと信じています。 – Deniz

答えて

2

セルフホストさOWINアプリケーションは、したがって、あなたがコンソールアプリケーションでそれを行うのと同じ方法で、例えば、いくつかの長い実行中のタスクをスピンオフすることができ、一般的に通常のコンソールアプリケーションです:

  • Task.Run
  • ThreadPool.QueueUserWorkItem IISで
  • new Thread(...).Start()

このような方法を使用することはお勧めできませんASP.NETアプリケーションを開催しましたアプリケーションプールは一般的にリサイクルされるため、アプリケーションがシャットダウンしてかなり頻繁に再起動されるためです。そのような場合、あなたのバックグラウンドタスクは中止されます。そのことを防ぐ(または延期する)ために、QueueBackgroundWorkItemのようなAPIが導入されました。

ただし、自己ホスト型でIISで実行されないため、上記のAPIを使用できます。

2

コントローラーの外にタスクを追加するためのメカニズムを提供する必要があります。

通常、アプリケーションでは実際のホストにはIISが使用されているので、「ホストされたプロセス」を使用できるとわかりましたが、コンソールアプリケーションではもっと簡単になる可能性があります。

あなたのDIフレームワークを約確認することができます知っているグローバルオブジェクト/シングルトンのいくつかの並べ替えに渡すことです頭に浮かぶ最も明白なアプローチは、常に

public class FooController : ApiController 
{ 
    ITaskRunner runner; 

    public FooController(ITaskRunner runner) { this.runner = runner; } 

    Public IActionResult DoStuff() { 
     runner.AddTask(() => { Stuff(); }); 
     return Ok(); 
    } 
} 
「ジョブのコンテナ」と同じオブジェクトであります

タスクランナーは、タスクの周りの単純なラッパー以上のことで、ジョブを実行できます。あなたが望むなら、Run()はそのフックを渡して渡され、要求が "処理され"コントローラがクリーンアップされた後でもタスクは "スコープ内"と見なされるので、アプリケーションはそれをきれいにしようとしています。

2

私は戦争の答えにコメントしたいと思っていましたが、自分の質問に答えることは長い説明のほうが良いようです。これは完全な答えではないかもしれません、申し訳ありません。

戦争の解決法がこの機能を実装する方法であり、私はそれに類似したことをしました。基本的には、タスクストアを作成し、新しいタスクが起動されるか完了するたびにこのタスクをタスクストアに追加/削除します。しかし、私はそのような単純ではないと信じているので、いくつかの問題について言及したいと思います。

1)シングルトンパターンを実装するために依存性注入フレームワークを使用することに同意します。私はそれを行うために、Autofacの 'SingleInstance()'メソッドを使用しました。しかし、インターネット上の多くの答えやリソースで見られるように、シングルトンパターンは時には不可欠であるように見えますが、一般的なパターンではありません。

2)リポジトリはスレッドセーフである必要があります。そのため、そのタスクストアからのタスクの追加/削除はスレッドセーフでなければなりません。ロックの代わりに 'ConcurrentDictionary'のような同時データ構造を使用することができます(これはCPU時間の点で高価な操作です)。

3)あなたの非同期にCancellationTokenを使用することを検討することができます。オペレーション。ユーザーがアプリケーションをシャットダウンしたり、実行時に処理できる例外が発生する可能性があります。機密データが失われ、そのような場合に適切に閉じられる可能性があるファイルのような異なるリソースを使用する場合は、長時間実行されるタスクが正常なシャットダウン/再開操作のボトルネックになる可能性があります。非同期ですが。トークンを使用してタスクを取り消そうとするとすぐにメソッドがキャンセルされることはありません。必要なときに長時間実行されるタスクを停止またはキャンセルするには、CancellationTokenまたは同様のメカニズムを試す価値があります。

4)データストアに新しいタスクを追加するだけでは不十分です。成功したかどうかにかかわらず、完了したタスクも削除してください。タスクの完了を知らせるイベントを発生させてから、このタスクをタスクストアから削除しました。しかし、私はそれに満足していません。なぜなら、イベントを発生させてからこのイベントにメソッドをバインドして、タスクの完了を受け取るのは、TPLでの続きのようなものです。それは、ホイールの再発明のようなものであり、イベントはマルチスレッド環境での卑劣な問題を引き起こす可能性があります。

これが役に立ちます。私は、コードを掲示する代わりにいくつかの経験を共有することを好む。なぜなら、私はこの問題に対する究極の解決策を持っているとは思わないからです。戦争の反応はおおまかなことですが、生産準備が整ったシステムでそれを行う前に、多くの問題を考慮する必要があります。

編集:長時間実行しているタスクについては、thisスレッドがあります。スレッドプールを管理することは良い習慣です。

+0

私の考えはまさに...あなたの実装についての仮定をするかもしれないし、しないかもしれない、あまりにも多くの背景情報で私の答えを駄目にしないようにしていました。完全な解決策を得るには、あなたのコードベースの残りの部分でパターン/コーディング標準を満たすほうが簡単なことを避けたいでしょう。スレッディングの懸案事項としては、データレイヤー/ビジネスロジックがこのレベルで処理するはずですが、スレッド/タスクを開始する心配はありません。私の2p ...あなたがそれを解決してうれしい:) – War

3

これは、Hangfireが作成したもの(https://www.hangfire.io/)とほぼ同じです。

音は忘れ去られた仕事のようです。

var jobId = BackgroundJob.Enqueue(
    () => Console.WriteLine("Fire-and-forget!")); 
2

あなたが探している回答がプッシュストリームコンテンツかもしれません。 PushStreamContentはレスポンスをストリーミングするのに役立ちます。実装に関するいくつかのアイデアを得るために、次のブログ記事を見てください。 STREAMING DATA WITH ASP .NET WEB API AND PUSHCONTENTSTREAM

関連する問題