微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

长时间运行的进程暂停

我在Visual Studio 2010 IDE中以debugging模式(F5)在Windows Server GoDaddy VPS上运行.NET 2.0控制台应用程序。

应用程序会定期冻结(就像垃圾收集器暂时暂停执行一样),但在罕见的情况下,它永远不会恢复执行!

我一直在调查这个问题已经好几个月了,而且已经没有想法了。

应用程序尽可能快地运行(它使用100%的cpu使用率),但是在正常的优先级。 它也是multithreading的。

当应用程序冻结时,我可以使用VS2010 IDE通过暂停/取消暂停进程(因为它正在debugging器中运行)来解冻它。

当我暂停冻结的过程时,上次执行的位置似乎不相关。

在冻结时,cpu使用率仍然是100%。

解冻后,它运行得非常好,直到下一次冻结。

服务器可能会在冻结之间运行70天,也可能只有24小时。

内存使用保持相对稳定; 没有任何内存泄漏的证据。

任何人都有任何提示诊断究竟发生了什么?

初始化和分区磁盘 – 如何防止“你需要格式化磁盘”的消息出现?

在Windows Vista> 8上获取用户帐户configuration文件图片

在Windows上持续集成Git

如何从.NET客户端应用程序加载URL

.Net的窗口菜单

检测Windows进程使用C#而不使用WMI启动和退出事件

如何在两个窗口服务之间形成依赖关系

#if(DEBUG)在定制的基类库中不起作用

使用.NET监视Windows 7中的声卡输出

了解.NET应用程序内存大小

它也是多线程的

这是问题的关键部分。 您正在描述一个非常典型的多线程程序可能会导致错误的方式。 它正在遭遇死锁,这是线程的典型问题之一。

它可以从信息进一步缩小,显然你的进程没有完全冻结,因为它仍然消耗100%的cpu。 您的代码中可能有一个热等待循环,这是一个在另一个线程上旋转的循环,指示事件。 这可能会导致一个特别讨厌的各种僵局,一个活锁 。 活锁对时序非常敏感,代码运行顺序的微小变化可能会使其进入活锁状态。 然后再退出

活锁非常难以调试,因为尝试这样做会使情况消失。 像附加一个调试器或打破代码,足以改变线程时序并将其赶出状态。 或者将日志语句添加代码中,这是调试线程问题的常用策略。 这会改变由于日志记录开销而导致的时间,从而使得活锁完全消失。

讨厌的东西,不可能得到像这样的网站这样的问题的帮助,因为它是非常依赖于代码。 通常需要仔细查看代码才能找到原因。 而且很少有重大的改写。 祝你好运。

应用程序是否有“死锁恢复/预防”代码? 也就是说,用timout锁定,然后再试一次,也许睡觉后?

应用程序是否检查错误代码(返回值或异常),并在出现错误的情况下反复重试?

请注意,这样的循环也可以通过事件循环发生,其中您的代码只在某个事件处理程序中。 它不一定是你自己的代码中的实际循环。 虽然这可能不是这种情况,如果应用程序被冻结,则指示阻塞的事件循环。

如果你有类似的东西,你可以尝试通过使超时和睡眠成为随机间隔来减轻这个问题,并且在错误可能产生死/活锁的情况下增加短暂的随机时间睡眠。 如果这样的循环是性能敏感的,添加一个计数器,并且只能随机启动睡眠,也许会在一些失败的重试次数增加间隔。 并确保您添加的任何睡眠不锁定时,没有睡觉。

如果情况会更频繁发生,您也可以使用它来平分您的代码并确定哪些循环(因为100%的cpu使用率意味着一些非常繁忙的循环正在旋转)。 但是,从问题的稀缺性来看,如果问题在实践中消失,我相信你会高兴的;)

这里有三件事情…

首先,开始使用.NET的服务器GC: http : //msdn.microsoft.com/en-us/library/ms229357.aspx 。 这可能会保持您的应用程序不被阻止。

其次,如果你可以在虚拟机上做到这一点:检查更新。 这似乎总是显而易见的,但我已经看到很多场合,简单的Windows更新修复了奇怪的问题。

第三,我想谈谈一个物体的生命周期,这可能是这里的一个问题。 这是一个很长的故事,所以忍受着我。

一个对象的生命周期基本上是建设 – 垃圾收集 – 定稿。 这三个进程都在一个单独的线程中运行。 GC将数据传递给具有调用“析构函数”的队列的终结线程。

那么如果你有一个终结器做了一些奇怪的事情,比如说:

public class FinalizerObject { public FinalizerObject(int n) { Console.WriteLine("Constructed {0}",n); this.n = n; } private int n; ~FinalizerObject() { while (true) { Console.WriteLine("Finalizing {0}...",n); System.Threading.Thread.Sleep(1000); } } }

因为终结器运行在一个单独的处理队列的线程中,所以让一个愚蠢的终结器对你的应用程序来说是一个严重的问题。 你可以通过使用上面的类来看到这个2次:

static void Main(string[] args) { SomeMethod(); GC.Collect(GC.MaxGeneration); GC.WaitForFullGCComplete(); Console.WriteLine("All done."); Console.ReadLine(); } static void SomeMethod() { var obj2 = new FinalizerObject(1); var obj3 = new FinalizerObject(2); }

注意,如果最终发生小内存泄漏,并且即使您的主线程仍在响应,也可以使用100%的cpu进程删除Thread.Sleep。 因为它们是不同的线程,所以从这里开始很容易阻塞整个进程 – 例如使用锁:

static void Main(string[] args) { SomeMethod(); GC.Collect(GC.MaxGeneration); GC.WaitForFullGCComplete(); Thread.Sleep(1000); lock (lockObject) { Console.WriteLine("All done."); } Console.ReadLine(); } static object lockObject = new Program(); static void SomeMethod() { var obj2 = new FinalizerObject(1,lockObject); var obj3 = new FinalizerObject(2,lockObject); } [...] ~FinalizerObject() { lock (lockObject) { while (true) { Console.WriteLine("Finalizing {0}...",n); System.Threading.Thread.Sleep(1000); } } }

所以我可以看到你在想“你是认真的吗? 事实是,你可能正在做这样的事情,甚至没有意识到这一点。 这就是“收益率”出现的地方:

来自'yield'的IEnumerable实际上是Idisposable,因此实现了Idisposable模式。 把你的'yield'实现和一个锁结合起来,忘记用'MoveNext'等来枚举Idisposable,你会得到一些反映上述情况的非常讨厌的行为。 特别是因为终结器是由一个单独的线程(!)从终止队列中调用的。 将它与一个无限循环或线程不安全的代码结合起来,你会得到一些非常令人讨厌的意外行为,这将在特殊情况下(当内存用完或GC应该做某事时)触发。

换句话说:我会检查你的一次性和终结者,并对他们非常批评。 检查'yield'是否有隐式终结器,并确保从同一个线程调用Idisposable。 你应该警惕一些事情的例子:

try { for (int i = 0; i < 10; ++i) { yield return "foo"; } } finally { // Called by Idisposable }

lock (myLock) // 'lock' and 'using' also trigger Idisposable { yield return "foo"; }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐