views:

154

answers:

1

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
    }
}
+1  A: 

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();
}
Benjamin Podszun
Could also host a WCF service in the Windows service, and have the ASP.NET app call that service. (A more structured variant on your MSMQ suggestion.)
itowlson
Sorry about the bNotDoneYet = false being missing; I missed it in the 'sanitization' of the code. Also, thanks for the sync samples. I don't do much these days in synchronization.
Walter Williams
@itowlson: Good point, missed that.
Benjamin Podszun
I got the communication to work using MSMQ. Thanks for the suggestions. On to the next issue...
Walter Williams