这是我上一个问题的后续跟进: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 object
和Monitor.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);
}
}
这似乎有效(我不再看错),但我不确定这是否是正确的方法......