.NET Windows服务需要使用STAThread
我创建了一个将调用某些 COM 组件的 Windows 服务,因此我将 [STAThread] 标记到 Main 函数。但是,当计时器触发时,它会报告 MTA 和 COM 调用失败。我该如何解决这个问题?
using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
using System.Timers;
namespace MyMonitorService
{
public class MyMonitor : ServiceBase
{
#region Members
private System.Timers.Timer timer = new System.Timers.Timer();
#endregion
#region Construction
public MyMonitor ()
{
this.timer.Interval = 10000; // set for 10 seconds
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.timer_Elapsed);
}
#endregion
private void timer_Elapsed (object sender, ElapsedEventArgs e)
{
EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);
}
#region Service Start/Stop
[STAThread]
public static void Main ()
{
ServiceBase.Run(new MyMonitor());
}
protected override void OnStart (string[] args)
{
EventLog.WriteEntry("MyMonitor", "My Monitor Service Started", EventLogEntryType.Information);
this.timer.Enabled = true;
}
protected override void OnStop ()
{
EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
this.timer.Enabled = false;
}
#endregion
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
服务由 Windows 服务托管系统运行,该系统使用 MTA 线程运行。你无法控制这个。您必须创建一个新的 线程 和 将其 ApartmentState 设置为 STA,并进行此操作线。
下面是一个扩展 ServiceBase 的类,它执行此操作:
请注意,此代码实际上并不停止服务,而是停止计时器。多线程上可能仍有大量工作正在完成。例如,如果您的工作包括在大型数据库上运行多个查询,您可能最终会崩溃,因为同时运行的线程太多。
在这种情况下,我会创建一定数量的 STA 线程(可能是开始时核心数量的 2 倍),用于监视工作项的线程安全队列。计时器滴答事件将负责向该队列加载需要完成的工作。
这一切都取决于您实际上每十秒正在做什么,是否应该在下次计时器计时时完成,在这种情况下您应该做什么等等。
Services are run by the windows service hosting system, which runs using MTA threads. You can't control this. You have to create a new Thread and set its ApartmentState to STA, and do your work on this thread.
Here's a class that extends ServiceBase that does this:
Note this code doesn't actually stop the service, it stops the timer. There could be lots of work still being done on multiple threads. For instance, if your work consisted of running multiple queries off a large database you may end up crashing because you have too many threads running at the same time.
In a situation like this, I'd create a set number of STA threads (maybe 2x the number of cores to start off with) which monitor a thread-safe queue for work items. The timer tick event would be responsible for loading that queue with the work needing done.
It all depends on what you're actually doing every ten seconds, whether or not it should be completed the next time the timer ticks, what you should do in this situation, etc etc.
这不能在服务中工作,调用 Main() 方法的线程已由服务管理器启动。您需要创建一个使用 Thread.SetApartmentState() 初始化的单独线程并泵送消息循环。
That cannot work in a service, the thread that calls your Main() method was already started by the service manager. You'll need to create a separate thread that is initialized with Thread.SetApartmentState() and pumps a message loop.
设置 STAThread 属性对服务不起作用。它的处理方式与应用程序不同,因此这将被忽略。
我的建议是手动为您的服务创建一个单独的线程,设置其单元状态,并将所有内容移入其中。这样,就可以正确地将线程设置为STA。
但是,这里还会出现另一个问题 - 您必须重新设计服务的工作方式。您不能只使用 System.Threading.Timer 用于计时的实例 - 它在单独的线程上运行,该线程不是 STA。当它的 elapsed 事件触发时,您将在另一个非 STA 线程上工作。
您可能希望在显式创建的线程中完成主要工作,而不是在计时器事件中完成工作。您可以在该阻塞线程中设置一个重置事件,并让您的计时器“设置”它以允许您的逻辑在 STA 线程中运行。
Setting the STAThread attribute will not work on a service. It's not being handled the same way as an application, so this will get ignored.
My recommendation would be to manually make a separate thread for your service, set its apartment state, and move everything into it. This way, you can set the thread to STA correctly.
However, there will be another issue here - you'll have to rework the way your service works. You can't just use a System.Threading.Timer instance for timing - it runs on a separate thread, which will not be STA. When its elapsed event fires, you'll be working on a different, non-STA thread.
Instead of doing your work in the timer event, you'll probably want to do your main work in the thread you create explicitly. You can have a reset event in that thread which blocks, and have your timer "set" it to allow your logic to run in the STA thread.
查看类似的示例: http://www.aspfree.com/c/a/C-Sharp/Creating-a-Windows-Service-with-C-Sharp-introduction/1/
如果您的主要是...
并将计时器开始/停止移出事件...
Looking at a similar example: http://www.aspfree.com/c/a/C-Sharp/Creating-a-Windows-Service-with-C-Sharp-introduction/1/
What if your main is...
and move your timer start / stop out of the events...
该报告表明它正在使用 STA。它基于 Will 的建议和 http://en.csharp -online.net/Creating_a_.NET_Windows_Service%E2%80%94Alternative_1:_Use_a_Separate_Thread
This reports that it is using STA. It is based on Will's suggestion and http://en.csharp-online.net/Creating_a_.NET_Windows_Service%E2%80%94Alternative_1:_Use_a_Separate_Thread