Passing strings to .NET Windows Service
I am writing a ASP.NET queue processor. Users will login and upload data files to the site, and then click to start processing the data files.
I have a Windows Service on the system that waits for items to arrive in the queue and processes them. So far everything works except the items in the queue seem to get lost. I believe the static members are losing scope, but I'm not sure how to fix it.
I thought about writing things to/from files, but the Status updates so often it would be a performance killer.
What's the best way to get data in and out of the service?
The Windows service is as follows:
using System;
using System.Collections.Generic;
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();
private static Queue<String> qProjectQueue = new Queue<String>();
private static Mutex mutexProjectQueue = new Mutex(false);
private Boolean bNotDoneYet = false;
#endregion
#region Properties
public static String Status { get; private set; }
#endregion
#region Construction
public MyMonitor ()
{
this.timer.Interval = 10000; // set for 10 seconds
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.timer_Elapsed);
Status = String.Empty;
}
#endregion
private void timer_Elapsed (object sender, ElapsedEventArgs e)
{
try
{
if (!this.bNotDoneYet)
{
this.bNotDoneYet = true;
for (;;)
{
MyMonitor.mutexProjectQueue.WaitOne();
if (MyMonitor.qProjectQueue.Count == 0)
{
EventLog.WriteEntry("MyMonitor", "The queue is empty", EventLogEntryType.Information);
break;
}
String strProject = MyMonitor.qProjectQueue.Dequeue();
EventLog.WriteEntry("MyMonitor", String.Format("The project {0} was dequeued", strProject), EventLogEntryType.Information);
MyMonitor.mutexProjectQueue.ReleaseMutex();
// Do something that updates MyMonitor.Status up to thousands of times per minute
}
}
this.bNotDoneYet = false;
}
catch (Exception ex)
{
EventLog.WriteEntry("MyMonitor", ex.Message, EventLogEntryType.Error);
}
}
public static void EnqueueProjects (params String[] astrProjects)
{
try
{
String strMessage = String.Format("The following projects were added to the queue:\n{0}", String.Join("\n", astrProjects));
EventLog.WriteEntry("MyMonitor", strMessage, EventLogEntryType.Information);
if (astrProjects == null)
开发者_如何转开发 return;
MyMonitor.mutexProjectQueue.WaitOne();
foreach (String strProject in astrProjects)
MyMonitor.qProjectQueue.Enqueue(strProject);
MyMonitor.mutexProjectQueue.ReleaseMutex();
}
catch (Exception e)
{
EventLog.WriteEntry("MyMonitor", e.Message, EventLogEntryType.Error);
}
}
#region Service Start/Stop
[STAThread]
public static void Main ()
{
ServiceBase.Run(new MyMonitor());
}
protected override void OnStart (string[] args)
{
try
{
EventLog.WriteEntry("MyMonitor", "MyMonitor Service Started", EventLogEntryType.Information);
this.timer.Enabled = true;
}
catch (Exception e)
{
EventLog.WriteEntry("MyMonitor", e.Message, EventLogEntryType.Error);
}
}
protected override void OnStop ()
{
try
{
EventLog.WriteEntry("MyMonitor", "MyMonitor Service Stopped", EventLogEntryType.Information);
this.timer.Enabled = false;
}
catch (Exception e)
{
EventLog.WriteEntry("MyMonitor", e.Message, EventLogEntryType.Error);
}
}
#endregion
}
}
Answering the general question how this could be solved (still unsure about the code at hand):
Depends on your requirements. From "easy" to "high-end":
- File system entries (with watcher or polling)
- Windows messages, SendMessage/PostMessage
- A shared database layer
- Message Queues (MS MQ for example)
Regarding your code:
First thought that crosses my mind: If the queue happens to be empty once you'll break out of the timer event and the bNotDoneYet
is never reset to false -> New entries wouldn't be considered?
In addition your producer/consumer pattern seems off for me. I'm used to a lightweight (and simplified):
Producer:
lock (_syncRoot) {
_queue.Enqueue(obj);
if (_queue.Count == 1) Monitor.Pulse(_syncRoot);
}
Consumer:
lock (_syncRoot) {
while (_queue.Count < 1) {
try {
Monitor.Wait(_syncRoot);
} catch (ThreadInterruptedException) {}
}
var obj = _queue.Dequeue();
}
精彩评论