作为大型自动化流程的一部分,我们正在调用第三方API,它可以在另一台计算机上调用服务。我们最近发现,当其他计算机不可用时,API调用有时会在尝试连接到远程服务器时旋转40分钟。
我们正在使用的API没有提供指定超时的方法,我们不希望我们的程序等待那么久,所以我认为线程是一种很好的方法来强制执行超时。生成的代码如下所示:
Thread _thread = new Thread(_caller.CallServices());
_thread.Start();
_thread.Join(timeout);
if (_thread.IsAlive)
{
_thread.Abort();
throw new Exception("Timed-out attempting to connect.");
}
基本上,我想让APICall()运行,但是如果它在超时过后仍然存在,则假设它将失败,将其终止并继续前进。
由于我刚接触C#和.net运行时的线程,我以为我会问两个相关的问题:
.net库中是否有更好/更合适的机制用于我正在尝试做的事情,并且我在那段代码中提交了任何线程陷阱吗?
答案 0 :(得分:5)
Thread.Abort()是线程中止的请求,并不保证它会及时执行。它也被认为是不好的做法(它会在被中止的线程中抛出线程中止异常,但看起来第三方API似乎没有其他选择。
如果您(以编程方式)知道远程服务主机的地址,则应在将控制权转移到第三方API之前对其进行ping操作。
如果不使用backgroundworker,可以将线程的IsBackgroundThread设置为true,这样就不会阻止程序终止。
答案 1 :(得分:3)
糟糕的主意。 Thread.Abort不一定能清除这种中断的API调用留下的混乱。
如果调用很昂贵,请考虑编写一个单独的执行调用的.exe,并使用命令行或临时文件将参数传递给它/从中传递参数。你可以比杀死一个线程更安全地杀死.exe。
答案 2 :(得分:3)
您也可以只使用委托...为执行工作的方法创建委托,然后在委托上调用BeginInvoke,向其传递参数,并使用回调函数来处理返回值(如果需要) ... 在BeginInvoke之后,您可以等待指定的时间让异步委托完成,如果它没有在指定的时间内继续,则继续......
public delegate [ReturnType] CallerServiceDelegate
([parameter list for_caller.CallService]);
CallerServiceDelegate callSvcDel = _caller.CallService;
DateTime cutoffDate = DateTime.Now.AddSeconds(timeoutSeconds);
IAsyncResult aR = callSvcDel.BeginInvoke([here put parameters],
AsynchCallback, null);
while (!aR.IsCompleted && DateTime.Now < cutoffDate)
Thread.Sleep(500);
if (aR.IsCompleted)
{
ReturnType returnValue = callSvcDel.EndInvoke(aR);
// whatever else you need to do to handle success
}
else
{
callSvcDel.EndInvoke(aR);
// whatever you need to do to handle timeout
}
注意:由于编写的AsynchCallback可能为null,因为代码从EndInvoke()中检索返回值,但是如果您愿意,可以让CallService()方法调用AsynchCallback委托并将返回值传递给它。 ..
答案 3 :(得分:0)
它可能有用,但没有人能够在不了解第三方API的情况下肯定地说。像这样中止线程可能会使组件处于无法恢复的某种无效状态,或者它可能不会释放它分配的资源(想想 - 如果你的一个例程刚刚停止执行中途,那该怎么办?你能对你的计划所处的状态做出任何保证吗?)。
正如Cicil建议的那样,首先ping服务器可能是个好主意。
答案 4 :(得分:0)
您的应用程序是长时间运行还是更像是按需运行的应用程序?如果是后者,我个人会考虑使用Thread.Abort()选项。虽然从纯粹主义者的角度来看它可能不是最理想的(资源管理等),但实施起来肯定是直截了当的,并且考虑到你的特定应用程序的工作方式,它可能会受到影响。
单独的可执行文件的想法是有道理的。也许另一种选择是使用AppDomains。我不是这方面的专家(我欢迎对此进行改进/更正),但据我了解,您将API调用放在单独的DLL中并将其加载到单独的AppDomain中。当API调用完成或您必须中止它时,您可以将DLL与DLL一起卸载。这个可能具有清理直接Thread.Abort()所不具备的资源的额外好处。