调用从Windows服务异步方法 [英] Calling async methods from a Windows Service

查看:200
本文介绍了调用从Windows服务异步方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Windows服务在C#编写定期触发关闭后台作业。通常情况下,在任何给定的时间,几十大量I / O密集​​型任务(下载大文件等)并行运行。服务于一​​个非常繁忙的Web服务器上运行(必要现在),我认为这可能在线程保护方面大大受益使用异步API的尽可能多的。

大部分工作已经完成。所有作业现在完全异步(利用HttpClient的,等),是主要的工作循环(重剂量Task.Delay的)。所有剩下的就是要弄清楚如何正确,安全地火起来从服务的的OnStart主循环。 Essentialy,这是备受警告说,有关调用 - 异步 - 从同步困境。下面是我到目前为止所(严重简化)。

在Program.cs的:

 静态无效的主要(字串[] args){
    TaskScheduler.UnobservedTaskException + =(发件人,E)=> {
        //日志和放大器;警报!
        e.SetObserved();
    };
    ServiceBase.Run(新的MyService());
}

在MyService.cs:

 保护覆盖无效的OnStart(字串[] args){
    _scheduler.StartLoopAsync(); //火灾和忘记!这将让我陷入麻烦?
}

这是为 StartLoopAsync 的呼叫令我担忧。我不能简单地等待()对返回的任务,因为需要的OnStart相对快速返回。 (工作循环需要在一个单独的线程中运行。)浮现在脑海中一对夫妇的想法:


  • 我是深受把该处理程序中主要就没有观察到异常覆盖?

  • 会不会有任何的好处是用Task.Run,​​像 Task.Run(()=> _scheduler.StartLoopAsync()等待());

  • 会不会有调用任何好处 _scheduler.StartLoopAsync()。ConfigureAwait(假)在这里? (我怀疑它,因为没有等待在这里。)

  • 会不会有什么好处,在这种情况下使用斯蒂芬·克利里的AsyncContextThread ?我还没有看到使用这种任何的例子,因为我开始一个无限循环,我不知道,同步备份到某些情况下甚至与此有关。


解决方案

UnobservedTaskException 将被用于所有未观测到工作例外,所以它用于记录这样的好地方。但是,这不是的伟大的,因为根据你的程序逻辑,你可能会看到虚假信息;例如,如果你 Task.WhenAny ,然后忽略较慢的任务,然后从慢任务的任何异常的的被忽略,但他们得到发送到 UnobservedTaskException 。作为替代方案,考虑将 ContinueWith 在顶层的任务。 StartLoopAsync 返回的) p>

您为 StartLoopAsync 调用看起来好像没什么问题,假设它是正确异步的。你可以使用 TaskRun (例如, Task.Run(()=> _scheduler.StartLoopAsync()) - 无等待是必要的),但唯一的好处是,如果 StartLoopAsync 本身可能引发一个异常(而不是断裂的返回的任务),或者如果时间太长,之前的第一个等待

ConfigureAwait(假)做的时候才有用一个等待,因为你猜测。

我的 AsyncContextThread 是专为这种情况,但它也设计得非常简单。 :) AsyncContextThread 提供了一个独立的线程与主循环类似于​​您的调度,完成一个的TaskScheduler TaskFactory 的SynchronizationContext 。然而,这是简单的:它只使用单个线程,并且所有的调度/上下文指回同一线程。我喜欢这一点,因为它大大简化了线程安​​全的关注,同时也允许并发异步操作的 - 但它没有充分利用线程池的话,例如,CPU密集型的工作会遮挡主循环(类似于UI线程的情况)。

在您的情况,这听起来像 AsyncContextThread 可以让你删除/简化一些$ C $的c您已经写。但在另一方面,它不是多线程喜欢你的解决方案。

I have a Windows Service written in C# that periodically fires off background jobs. Typically, at any given time, several dozen heavily I/O bound Tasks (downloading large files, etc) are running in parallel. The service runs on a relatively busy web server (necessary for now), and I think it could benefit greatly in terms of thread conservation to use asynchronous APIs as much as possible.

Most of this work is done. All jobs are now fully async (leveraging HttpClient, etc.), as is the main job loop (with heavy doses of Task.Delay). All that's left is to figure out how to correctly and safely fire up the main loop from the service's OnStart. Essentialy, it's the much-warned-about calling-async-from-sync dilemma. Below is what I have so far (grossly simplified).

in Program.cs:

static void Main(string[] args) {
    TaskScheduler.UnobservedTaskException += (sender, e) => {
        // log & alert!
        e.SetObserved();
    };
    ServiceBase.Run(new MyService());
}

in MyService.cs:

protected override void OnStart(string[] args) {
    _scheduler.StartLoopAsync(); // fire and forget! will this get me into trouble?
}

It's that call to StartLoopAsync that concerns me. I can't simply Wait() on the returned Task because OnStart needs to return relatively quickly. (Job loops need to run on a separate thread.) A couple thoughts come to mind:

  • Am I well covered as far as unobserved exceptions by placing that handler in Main?
  • Would there be any benefit to using Task.Run, something like Task.Run(() => _scheduler.StartLoopAsync().Wait()); ?
  • Would there be any benefit to calling _scheduler.StartLoopAsync().ConfigureAwait(false) here? (I'm doubting it since there's no await here.)
  • Would there be any benefit to using Stephen Cleary's AsyncContextThread in this situation? I haven't seen any examples of using this, and since I'm starting an infinite loop I don't know that syncing back up to some context is even relevant here.

解决方案

UnobservedTaskException will be called for all unobserved Task exceptions, so it's a good place for logging like this. However, it's not great because, depending on your program logic, you may see spurious messages; e.g., if you Task.WhenAny and then ignore the slower task, then a any exceptions from that slower task should be ignored but they do get sent to UnobservedTaskException. As an alternative, consider placing a ContinueWith on your top-level task (the one returned from StartLoopAsync).

Your call to StartLoopAsync looks fine to me, assuming it's properly asynchronous. You could use TaskRun (e.g., Task.Run(() => _scheduler.StartLoopAsync()) - no Wait is necessary), but the only benefit would be if StartLoopAsync itself could raise an exception (as opposed to faulting its returned task) or if it took too long before the first await.

ConfigureAwait(false) is only useful when doing an await, as you surmised.

My AsyncContextThread is designed for this kind of situation, but it was also designed to be very simple. :) AsyncContextThread provides an independent thread with a main loop similar to your scheduler, complete with a TaskScheduler, TaskFactory, and SynchronizationContext. However, it is simple: it only uses a single thread, and all of the scheduling/context points back to that same thread. I like that because it greatly simplifies thread safety concerns while also allowing concurrent asynchronous operations - but it is not making full use of the thread pool so, e.g., CPU-bound work would block the main loop (similar to a UI thread scenario).

In your situation, it sounds like AsyncContextThread may let you remove/simplify some of the code you've already written. But on the other hand, it is not multithreaded like your solution is.

这篇关于调用从Windows服务异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆