我有一个偶尔挂起的方法(在 dll 中我无法修改但必须使用(。 如果我再次运行它,它通常会正常工作。 我想知道是否可以制作一个后台线程,等待 20 分钟,然后在我的程序中抛出异常。
var triesLeft = 5;
while (triesLeft > 0) {
try {
var t = new Thread(() => { wait(20 minutes); throw new ApplicationHungException();})
t.Start();
Object o = MethodThatHangsForever10PercentOfTheTime();
} catch (ApplicationHungException e) {
triesLeft--;
}
}
t.Abort();
这不起作用,因为异常不会传递到它所包含的 try catch 块。 有没有办法让线程将其异常提供给 try catch 块?
一种方法是在单独的线程中触发错误的方法,并等待以下两种情况之一发生;
- 线程完成,或
- 经过预定的时间量(例如20分钟(
一旦发生上述任何一种情况,我们都可以采取适当的行动。
代码如下所示:
static void DoProcessing() {
var triesLeft = 5;
Object o = null;
while (triesLeft > 0) {
var t = new Thread(() => { o = MethodThatHangsForever10%OfTheTime(); }).Start();
if (t.Join(new TimeSpan(0, 20, 0))) {
// The thread completed
break;
} else {
// We're out of time.
t.Abort(); // Important: See comments below about this
triesLeft--;
}
}
}
事实证明,中止线程是一项危险且脆弱的操作,正如 Reed Copsey 在下面的评论中指出的那样。您的替代方法是允许挂起的线程在其余生中存活(无论可能有多长(,或者将 Heisenbug 方法调用隔离到单独的进程中。
然而,这打开了另一个蠕虫罐,因为您必须处理进程间通信,数据序列化和同步。这可能值得,也可能不值得,这是我留给你的判断电话。
您可以在单独的线程中完成工作,并等待 20 分钟以使其完成:
var triesLeft = 5;
while (triesLeft > 0)
{
var mre = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(_ => {
MethodThatHangsForever10PercentOfTheTime();
mre.Set();
});
if (mre.WaitOne(TimeSpan.FromMinutes(20)))
{
break; // Success!
}
triesLeft--;
}
}