1

I've seen several posts on SO that are similar to my question, but none of them have solved my problem. I'm creating a windows service that is going to poll a Redis database every few seconds or so and perform an action based off of the result. I'd like to create a "thread pool" of sorts so that I can run multiple actions at the same time if I get a result from Redis while another command is being processed (on another thread).

One of my main problems is that when I stop my Windows service, the process still stays alive for ~30 seconds or so instead of closing down. Here are the relevant code snippets:

Thread Worker;
IDatabase db = ...;
AutoResetEvent StopRequest = new AutoResetEvent(false);

protected override void OnStart(string[] args) {
    var poller = new Poller();
    Worker = new Thread(() => poller.Poll(StopRequest));
    Worker.Start();
}
protected override void OnStop() {
    // Signal worker to stop and wait until it does
    StopRequest.Set();
    Worker.Join();
}

Here's an example of the Poller classes Poll method.

public async void Poll(AutoResetEvent finished)
{
    var res = string.Empty;

    while (!finished.WaitOne(1000))
    {
        res = db.StringGet($"task");
        if (!String.IsNullOrEmpty(res))
        {
            ParseAction(res);
        }

        db.KeyDelete($"task");
    }
}

So this code (with a lot trimmed out) stays running in the background correctly, and seems to process incoming queries from Redis just fine, but I'm having the issue with the process not closing correctly as I mentioned above. I'm also not sure if this is the best approach to take for this situation. I'd love some pointers on better or more "idiomatic" ways to handle this threading issue.

Thanks!

jewnbug97
  • 327
  • 7
  • 14

2 Answers2

1

A better way to deal with Windows service is to move entire processing into a background task. That will allow you to handle startup and shutdown much more gracefully.

And if you use Task to simulate polling, then you can use CancellationToken to propagate shutdown event to other layers of processing. Here you can find how to simulate timer using Task. Please read Is there a Task based replacement for System.Threading.Timer?

Here is the code sample of windows service OnStart and OnStop handlers with background task that starts and shuts down quickly. This code is based on .NET 4.6.1.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.ServiceProcess;

namespace TEST.MY.SERVICE
{
    partial class MyService : ServiceBase
    {
      private Task _initializationTask;
      private CancellationTokenSource _initializationCancelTokenSource;
      private CancellationToken _intitializationCancellationToken;

      public MyService()
      {
          InitializeComponent();
      }

      protected override void OnStart(string[] args)
      {
        _initializationCancelTokenSource = new CancellationTokenSource();
        _intitializationCancellationToken = _initializationCancelTokenSource.Token;
        _initializationTask = Task.Run(() =>
        {
          //Kick off polling from here that also uses _intitializationCancellationToken, so that when _initializationCancelTokenSource.Cancel() is invoked from OnStop it will start cancellation chain reaction to stop all running activity. You can pass it even into your methods and check _intitializationCancellationToken.IsCancellationRequested and take appropriate actions.

                //using the Task timer from the other stack overflow post, You could do something like
                Task perdiodicTask = PeriodicTaskFactory.Start(() =>
                {
                    Console.WriteLine(DateTime.Now);
                    //execute your logic here that has to run periodically
                }, intervalInMilliseconds: 5000, // fire every 5 seconds...
                   cancelToken: _intitializationCancellationToken); // Using same cancellation token to manage timer cancellation

                perdiodicTask.ContinueWith(_ =>
                {
                    Console.WriteLine("Finished!");
                }).Wait();

        }, _intitializationCancellationToken)
        .ContinueWith(t =>
        {
          //deal with any task related errors
        },TaskContinuationOptions.OnlyOnFaulted);
      }

      protected override void OnStop()
      {
        try
         {
           _initializationCancelTokenSource?.Cancel();
           _initializationCancelTokenSource?.Dispose();
           _initializationTask?.Dispose();
          }
          catch (Exception stopException)
          {
                    //log any errors
          }
      }
  }
}

Here you can find more details about how to cancel a waiting task. https://msdn.microsoft.com/en-us/library/dd321315(v=vs.110).aspx

This should give you a good idea on how to design your windows service. Make necessary tweeks for your needs. Get yourself familiarize with c# Task library.

Vinod
  • 1,882
  • 2
  • 17
  • 27
-1

have you pondered using a Boolean/Binary flag to find out if the service is in fact running? or perhaps performing a Call within the start of the Loop to check? I'm not familiar enough with C# in order to fully comprehend the entire task at hand, but I know that when Multi-Threading is involved, Binary/Boolean Flags are on average rather Stable.

For Example, I play a Steam Game that is in Beta (Space Engineers) that uses C# and it seems to consistently have problems with Multi-Threading errors and clearing Parent Data after every execution, but the Mod Authors on Steam Workshop have a Tendency of using Boolean and Binary Flags in order to ensure their tasks don't get stuck or crash because the Load Times to relaunch the Game are horrific, so they attempt to avoid as many crashes as possible.

It might not be Fancy, but as long as you ensure it doesn't create a runaway Memory Leak, you should be fine. I recommend, if using an Incremental Variable for your Unique Identifier for each Task, to Explicitly set an Upper Limit somewhere, and when said limit is reached, it will call that Task and reset the Incremental Variable to Zero (with lots of Buffer Space to prevent Accidental Data Loss).

If the Task is running, it will perform the call, set the Boolean, and execute, might desire another call to verify the task is still running before attempting to write to the destination, as I am assuming that without the Task, the Information does nothing, and if the Task isn't running, it will delve into the if != isRunning and get sent to the correct destination to kill the Thread.

I hope this information is helpful to you, as I mentioned before, I'm only a beginner in C#, so I'm not as familiar with the Advanced Commands as some of the other Users on here.

Blue64
  • 1
  • 3