带并行任务的Windows服务 - 优雅地停止服务

时间:2014-04-01 07:31:17

标签: c# parallel-processing task-parallel-library

这是我上一个问题的后续跟进:Writing a sync application using Windows Service to process files in Parallel

我已经准确地按照我的要求正确设置了时间和处理。当安装该服务并启动它时,它正在做一些工作,然后随机停止,我有一个Windows服务停止错误,我在事件查看器中发现以下错误:

Application: SyncTest.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AggregateException
Stack:
   at System.Threading.Tasks.Parallel.ForWorker[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](Int32, Int32, System.Threading.Tasks.ParallelOptions, System.Action`1<Int32>, System.Action`2<Int32,System.Threading.Tasks.ParallelLoopState>, System.Func`4<Int32,System.Threading.Tasks.ParallelLoopState,System.__Canon,System.__Canon>, System.Func`1<System.__Canon>, System.Action`1<System.__Canon>)
   at System.Threading.Tasks.Parallel.ForEachWorker[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](System.Collections.Generic.IList`1<System.__Canon>, System.Threading.Tasks.ParallelOptions, System.Action`1<System.__Canon>, System.Action`2<System.__Canon,System.Threading.Tasks.ParallelLoopState>, System.Action`3<System.__Canon,System.Threading.Tasks.ParallelLoopState,Int64>, System.Func`4<System.__Canon,System.Threading.Tasks.ParallelLoopState,System.__Canon,System.__Canon>, System.Func`5<System.__Canon,System.Threading.Tasks.ParallelLoopState,Int64,System.__Canon,System.__Canon>, System.Func`1<System.__Canon>, System.Action`1<System.__Canon>)
   at System.Threading.Tasks.Parallel.ForEachWorker[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Threading.Tasks.ParallelOptions, System.Action`1<System.__Canon>, System.Action`2<System.__Canon,System.Threading.Tasks.ParallelLoopState>, System.Action`3<System.__Canon,System.Threading.Tasks.ParallelLoopState,Int64>, System.Func`4<System.__Canon,System.Threading.Tasks.ParallelLoopState,System.__Canon,System.__Canon>, System.Func`5<System.__Canon,System.Threading.Tasks.ParallelLoopState,Int64,System.__Canon,System.__Canon>, System.Func`1<System.__Canon>, System.Action`1<System.__Canon>)
   at System.Threading.Tasks.Parallel.ForEach[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Action`1<System.__Canon>)
   at SyncTest.StartProcess(System.Object)
   at System.Threading.TimerQueueTimer.CallCallbackInContext(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.TimerQueueTimer.CallCallback()
   at System.Threading.TimerQueueTimer.Fire()
   at System.Threading.TimerQueue.FireNextTimers()
   at System.Threading.TimerQueue.AppDomainTimerCallback()

到目前为止,这是我的代码:

public partial class Service : ServiceBase
{
    static Timer _SyncTimer;
    static Object _SyncLock = new Object();

    public Service()
    {
        InitializeComponent();

        // Init
        ConsoleLog("--- Sync Initializing ---");
    }

    protected override void OnStart(string[] args)
    {
        // Every 5 Seconds
        _SyncTimer = new Timer(StartProcess, null, 0, 5000);
    }

    protected override void OnStop()
    {
        // Stop Timer
        if (null != _SyncTimer)
        {
            ConsoleLog("### Stopping Process ###");
            try
            {
                _SyncTimer.Dispose();
            }
            catch { }
            finally
            {
                _SyncTimer = null;
            }
        }
    }

    static void StartProcess(Object state)
    {
        ConsoleLog("### Starting Process ###");
        bool lockTaken = false;
        try
        {
            Monitor.TryEnter(_SyncLock, ref lockTaken);
            if (lockTaken)
            {
                ConsoleLog("Lock Acquired. Doing some dummy work...");

                List<string> fileList = new List<string>()
                {
                    "fileA.csv",
                    "fileB.csv"
                };

                Parallel.ForEach(fileList, (string fileName) =>
                {
                    ConsoleLog("Processing File: " + fileName);
                    Thread.Sleep(10000); // 10 sec to process each file
                });

                GC.Collect();
            }
            else
                ConsoleLog("Sync Is Busy, Skipping Cycle");
        }
        finally
        {
            if (lockTaken)
                Monitor.Exit(_SyncLock);
        }
    }

    static void ConsoleLog(String Message)
    {
        using (StreamWriter sw = File.AppendText(@"C:\SyncTest\application.log"))
        {
            sw.WriteLine("[{0}]: {1}",
                DateTime.Now.ToString("dd/mm/yyyy HH:mm:ss tt"),
                Message);
        }
    }
}

它确实似乎正确启动和停止了Windows服务:

[01/22/2014 08:22:58 AM]: --- Sync Initializing ---
[01/22/2014 08:22:58 AM]: ### Starting Process ###
[01/22/2014 08:22:58 AM]: Lock Acquired. Doing some dummy work...
[01/22/2014 08:22:58 AM]: Processing File: fileA.csv
[01/22/2014 08:22:58 AM]: Processing File: fileB.csv
[01/23/2014 08:23:04 AM]: ### Starting Process ###
[01/23/2014 08:23:04 AM]: Sync Is Busy, Skipping Cycle
[01/23/2014 08:23:06 AM]: ### Stopping Process ###
[01/23/2014 08:23:50 AM]: --- Sync Initializing ---
[01/23/2014 08:23:50 AM]: ### Starting Process ###
[01/23/2014 08:23:50 AM]: Lock Acquired. Doing some dummy work...
[01/23/2014 08:23:50 AM]: Processing File: fileA.csv
[01/23/2014 08:23:50 AM]: Processing File: fileB.csv
[01/23/2014 08:23:55 AM]: ### Starting Process ###
[01/23/2014 08:23:55 AM]: Sync Is Busy, Skipping Cycle
[01/23/2014 08:23:58 AM]: ### Stopping Process ###

如何优雅地停止同步?即允许当前Parallel.Foreach在同步停止计时器之前完成并停止同步“循环”?


更新

在做了一些进一步的阅读后,我在某处读到你可以使用lock objectMonitor.TryEnter等待当前进程完成,然后退出:

protected override void OnStop()
{
    ConsoleLog("### Stopping Process ###");
    bool lockTaken = false;
    try
    {
        Monitor.TryEnter(_SyncLock, ref lockTaken);
        if (lockTaken)
        {
            try
            {
                _SyncTimer.Dispose();
            }
            catch { }
            finally
            {
                _SyncTimer = null;
            }
            GC.Collect();
        }
        else
            ConsoleLog("Sync Is Busy, Waiting To Stop");
    }
    finally
    {
        if (lockTaken)
            Monitor.Exit(_SyncLock);
    }
}

这似乎有效(我不再看错),但我不确定这是否是正确的方法......

0 个答案:

没有答案