如何处理第三方代码中的死锁



我们有一个第三方方法Foo,它有时由于未知原因而在死锁中运行。

我们正在执行一个单线程tcp服务器,并每30秒调用一次此方法来检查外部系统是否可用。

为了缓解第三方代码中的死锁问题,我们将ping调用放在Task.Run中,以便服务器不会死锁。

async Task<bool> WrappedFoo()
{
var timeout = 10000; 
var task = Task.Run(() => ThirdPartyCode.Foo());
var delay = Task.Delay(timeout);
if (delay == await Task.WhenAny(delay, task ))
{
return false;
}
else
{
return await task ;
}
}

但这(在我们看来(有可能使自由线程的应用程序匮乏。因为如果一次调用ThirdPartyCode.Foo死锁,线程将永远无法从该死锁中恢复,如果这种情况经常发生,我们可能会耗尽资源。

有没有一种通用的方法应该如何处理死锁的第三方代码?

CancellationToken不起作用,因为第三方api不提供任何取消选项。

更新:手头的方法来自SAP提供的SAPNCO.dll,用于建立和测试与SAP系统的rfc连接,因此该方法不是简单的网络ping。我重新命名了问题中的方法,以避免进一步误解

是否有一种通用的方法来处理死锁的第三方代码?

是的,但这并不容易或简单。

行为不端的代码的问题在于,它不仅会泄漏资源(例如线程(,而且还会无限期地占用重要资源(例如,一些内部的"句柄"或"锁"(。

强制回收线程和其他资源的唯一方法是结束进程。操作系统用于清理行为不端的进程,并且非常擅长。因此,这里的解决方案是启动一个子进程来执行API调用。主应用程序可以通过重定向stdin/stdout与其子进程进行通信,如果子进程超时,主应用程序就可以终止它并重新启动它。

不幸的是,这是取消不可取消代码的唯一可靠方法。

取消任务是一种协作操作,将CancellationToken传递给所需的方法,并在外部使用CancellationTokenSource.Cancel:

public void Caller()
{
try
{
CancellationTokenSource cts=new CancellationTokenSource();
Task longRunning= Task.Run(()=>CancellableThirdParty(cts.Token),cts.Token);
Thread.Sleep(3000); //or condition /signal
cts.Cancel();
}catch(OperationCancelledException ex)
{
//treat somehow
}

}
public void CancellableThirdParty(CancellationToken token)
{
while(true)
{
// token.ThrowIfCancellationRequested()  -- if you  don't treat the cancellation here
if(token.IsCancellationRequested)
{
// code to treat the cancellation signal
//throw new OperationCancelledException($"[Reason]");
}
}
}

正如您在上面的代码中看到的,为了取消正在进行的任务,在其中运行的方法必须围绕CancellationToken.IsCancellationRequested标志或简单的CancellationToken.ThrowIfCancellationRequested方法进行结构化,使得主叫方仅发出CCD_ 9。

不幸的是,如果第三方代码不是围绕CancellationToken设计的(它不接受CancellationToken参数(,那么你就无能为力了

您的代码没有取消被阻止的操作。使用CancellationTokenSource并将取消令牌传递给Task.Run

var cts=new CancellationTokenSource(timeout);
try
{
await Task.Run(() => ThirdPartyCode.Ping(),cts.Token);
return true;
}
catch(TaskCancelledException)
{
return false;
}

阻塞很可能是由于网络或DNS问题引起的,而不是实际的死锁。

这仍然浪费了等待网络操作完成的线程。您可以使用.NET自己的Ping.SendPingAsync异步Ping指定超时:

var ping=new Ping();
var reply=await ping.SendPingAsync(ip,timeout);
return reply.Status==IPStatus.Success;

PingReply类包含比简单的成功/失败更详细的信息。Status属性单独区分路由问题、无法到达的目的地、超时等

最新更新