TyC*_*obb 6 .net c# multithreading windows-services
我有一个Windows服务,每5秒检查一次工作.它System.Threading.Timer用于处理检查和处理,Monitor.TryEnter并确保只有一个线程正在检查工作.
假设它必须是这种方式,因为以下代码是由服务创建的8个其他工作程序的一部分,并且每个工作程序都有自己需要检查的特定类型的工作.
readonly object _workCheckLocker = new object();
public Timer PollingTimer { get; private set; }
void InitializeTimer()
{
if (PollingTimer == null)
PollingTimer = new Timer(PollingTimerCallback, null, 0, 5000);
else
PollingTimer.Change(0, 5000);
Details.TimerIsRunning = true;
}
void PollingTimerCallback(object state)
{
if (!Details.StillGettingWork)
{
if (Monitor.TryEnter(_workCheckLocker, 500))
{
try
{
CheckForWork();
}
catch (Exception ex)
{
Log.Error(EnvironmentName + " -- CheckForWork failed. " + ex);
}
finally
{
Monitor.Exit(_workCheckLocker);
Details.StillGettingWork = false;
}
}
}
else
{
Log.Standard("Continuing to get work.");
}
}
void CheckForWork()
{
Details.StillGettingWork = true;
//Hit web server to grab work.
//Log Processing
//Process Work
}
Run Code Online (Sandbox Code Playgroud)
现在问题出
在上面:上面的代码允许2个Timer线程进入该CheckForWork()方法.老实说,我不明白这是怎么可能的,但是我已经在这个软件运行的多个客户端体验过这个.
我今天推送一些工作时得到的日志显示它检查了两次工作,我有2个线程独立尝试处理,这导致工作失败.
Processing 0-3978DF84-EB3E-47F4-8E78-E41E3BD0880E.xml for Update Request. - at 09/14 10:15:501255801
Stopping environments for Update request - at 09/14 10:15:501255801
Processing 0-3978DF84-EB3E-47F4-8E78-E41E3BD0880E.xml for Update Request. - at 09/14 10:15:501255801
Unloaded AppDomain - at 09/14 10:15:10:15:501255801
Stopping environments for Update request - at 09/14 10:15:501255801
AppDomain is already unloaded - at 09/14 10:15:501255801
=== Starting Update Process === - at 09/14 10:15:513756009
Downloading File X - at 09/14 10:15:525631183
Downloading File Y - at 09/14 10:15:525631183
=== Starting Update Process === - at 09/14 10:15:525787359
Downloading File X - at 09/14 10:15:525787359
Downloading File Y - at 09/14 10:15:525787359
Run Code Online (Sandbox Code Playgroud)
这些日志是异步写入并排队的,所以不要过于深入研究时间匹配的事实,我只是想指出我在日志中看到的内容,表明我有2个线程命中了一段代码我相信应该永远不会被允许.(日志和时间都是真实的,只是消毒过的消息)
最终会发生的是,2个线程开始下载一个足够大的文件,其中一个文件最终在文件上被拒绝访问并导致整个更新失败.
上面的代码怎么能实际允许这个呢?我去年经历过这个问题,当时我有一个lock而不是Monitor并且假设它只是因为计时器最终开始得到足够的偏移,因为lock阻塞我得到了计时器线程堆叠,即一个阻塞了5秒并且直接通过Timer正在触发另一个回调,它们都以某种方式进入了.这就是为什么我Monitor.TryEnter选择了这个选项,所以我不会只是继续堆叠定时器线程.
任何线索?在以前我试图解决这个问题的所有情况下,这System.Threading.Timer是一个常数,我认为它是根本原因,但我不明白为什么.
TL;DR
生产存储过程已经很多年没有更新了。工作人员正在得到他们本不应该得到的工作,因此多个工作人员正在处理更新请求。
我终于找到时间在本地正确设置自己,通过 Visual Studio 充当生产客户端。虽然我无法像我所经历的那样重现它,但我确实偶然发现了这个问题。
那些假设多个工人正在接手工作的假设确实是正确的,但这是永远不可能发生的事情,因为每个工人在他们所做和要求的工作中都是独一无二的。
事实证明,在我们的生产环境中,用于根据工作类型检索工作的存储过程在部署多年(是的,多年!)后并未更新。任何检查工作的内容都会自动获得更新,这意味着当 Update 工作人员和工作人员 Foo 同时检查时,它们最终都会完成相同的工作。
值得庆幸的是,修复是数据库端的,而不是客户端更新。
| 归档时间: |
|
| 查看次数: |
365 次 |
| 最近记录: |