There was a problem with the windows service. I have a windows service that performs complex statistical analysis with data transfer between several servers. The analysis is performed on a timer ( system.timers.timer ) in the OnStart method. The execution time of one analysis is quite long (it can reach 20 minutes). The problem is stopping this service. If you try to stop it at the moment when the analysis is started, then it hangs tightly and it becomes very problematic to start or remove it from the system. If, however, to stop it at the time of inactivity, it successfully completes its work.

The service has the OnStop method, but I have little OnStop how to stop the running analysis using this method. Advise any solution to this problem.

    2 answers 2

    Possible crutches:

    1. do background thread thread.abort ()
    2. set background thread IsBackground = true

    In both cases, you will have to abandon the standard timer in favor of your thread. In this thread you will have to twist an infinite loop with Thread.Sleep () between calls.

    These crutches are bad because they can leave the background process in the wrong state. For example, he can write in the database half of the information, and the rest do not have time or something like that. But if the nature of the process is such that it can be interrupted at any time - this method can be used.


    Now about the right way. The cause of the problem is the following.

    The main method of your service most likely looks like this:

     static void Main() { ServiceBase.Run(new FooService()); } 

    When FooService receives a stop signal, it is marked as completed, and the ServiceBase.Run exits. This leads to the termination of the Main method - but the background thread still remains.

    What does the service manager see? The process continues to work - therefore it is impossible to launch a new one. But the old process does not accept the control signals ...

    In order to avoid this situation, you must perform two actions.

    1. Correctly interrupt the analysis task when a Stop signal is received.
    2. Wait until the background task is finished before exiting OnStop.

    The first item is needed. so that the service can be completed in less than 20 minutes, the second - so that it does not look hung up.


    In order to correctly interrupt the task of analysis - it is necessary to provide such an interruption in the analysis algorithm itself. For example, get some boolean flag and periodically check it.

    But there is a better way, and it is called CancellationToken . Well, in order to wait for the end of processing, it is convenient to create a new thread and wait for it.

    Here is an example of such a service:

     class FooService { private readonly CancellationTokenSource ctsStop = new CancellationTokenSource(); private Thread thread; protected override OnStart(string[] args) { thread = new Thread(ThreadProc) { IsBachground = false }; thread.Start(ctsStop.Token); } protected override void OnStop() { ctsStop.Cancel(); // Отменяем выполнение задачи, если она запущена thread.Join(); } private void ThreadProc(object obj) { var token = (CancellationToken)obj; do { RunTask(token); } while (!token.WaitHandle.WaitOne(...)); } private void RunTask(CancellationToken stopToken) { // тут ваша аналитика } } 

    In the course of the task execution, it is necessary to periodically check stopToken.IsCancellationRequested , and if it stopToken.IsCancellationRequested true, then it is necessary to quickly wrap it up.

    If you need to wait, then instead of Thread.Sleep it is better to make stopToken.WaitHandle.WaitOne .

    Well, plus you can subscribe to the cancel event by making stopToken.Register (but to avoid memory leaks, all such calls should be made in the using block).

      You can not stop the running code just because the code must cooperate . For example, you can use a boolean flag with the meaning “you need to stop computing”, and check it in the computing code. (Remember to protect the lock variable for multi-threaded access.)

      Or you can use the CancellationToken , it is specifically designed for this.