![](/img/trans.png)
[英]How to handle recaptcha on third-party site in my client application
[英]How to handle a deadlock in third-party code
我們有一個第三方方法Foo
,它有時會因為未知原因而陷入死鎖。
我們正在執行一個單線程 tcp-server 並每 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 與其子進程通信,如果子進程超時,主應用程序可以終止它並重新啟動它。
不幸的是,這是取消不可取消代碼的唯一可靠方法。
您的代碼沒有取消被阻止的操作。 使用 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 class 包含比簡單的成功/失敗更詳細的信息。 Status 屬性單獨區分路由問題、無法到達的目的地、超時等
取消任務是一種協作操作,您將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
方法構建,以便調用者只需發出CancellationTokenSource.Cancel
。
不幸的是,如果第三方代碼不是圍繞CancellationToken
設計的(它不接受CancellationToken
參數),那么您無能為力。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.