有時候我們調用一個第三方的會阻塞的方法,我們要想法做一個調用逾時值,一般來說就是另起一個線程加join的辦法,這裡有另外一種思路,但也不是完全的解決辦法,希望大家多多討論。
下面是咱們的一個方法,很簡單,一個執行方法,一個終止方法,一個和執行方法簽名相同的公開委託。
class MyHelper
{
public delegate int ExecuteDelegate(int a);
volatile bool _stopFlag = false;
public int Execute(int a)
{
Console.WriteLine("Execute方法執行線程:{0}",Thread.CurrentThread.GetHashCode());
int i = 0;
while (!_stopFlag)
{
Thread.Sleep(1000);
if(++i >= a)
return i;
}
return i;
}
public void AbortExecute()
{
_stopFlag = true;
Console.WriteLine("終止操作");
}
}
上面的類的Excute方法是阻塞的,如果傳入的a參數是一個特別大的值,調用這個方法的代碼會阻塞很長的時間,如果用戶端的很多請求都要調用這個方法,而且你的系統實現了多線程,用線程池線程來處理使用者的每一個請求,這就有問題了,沒過多久,你的線程池就耗盡了。這時候用下面的辦法可以加上一個逾時邏輯。
private static void InvokeExcute()
{
MyHelper h = new MyHelper();
MyHelper.ExecuteDelegate d = h.Execute;
IAsyncResult result = (IAsyncResult)d.BeginInvoke(5,
delegate(IAsyncResult ar)
{
int temp = d.EndInvoke(ar);
Console.WriteLine("回調方法執行線程:{0}", Thread.CurrentThread.GetHashCode());
Console.WriteLine("執行結果是:{0}", temp);
}, h);
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
delegate(object state, bool timedOut)
{
if (timedOut)
{
try
{
((MyHelper)state).AbortExecute();
}
catch (Exception ex)
{
Trace.TraceError("終止操作出錯:{0}",ex);
}
Console.WriteLine("逾時處理線程:{0},已逾時",Thread.CurrentThread.GetHashCode());
}
else
{
Console.WriteLine("逾時處理線程:{0},未逾時", Thread.CurrentThread.GetHashCode());
}
},h, 3000, true);
}
以上的範例程式碼調用的Excute方法要執行5秒,而我們設定了3秒的逾時時間,到了3秒咱們就終止調用。以後如果自己要寫這種可能會阻塞的方法就加一個Abort的方法來終止操作並清理資源,像HttpWebRequest類就是這麼設計的,有一個Abort方法。
但是:如果這個會阻塞的方法不是你寫的,是第三方提供的,而且還沒有Abort方法,這時候雖然RegisterWaitForSingleObject的逾時回調會執行,但是BeginInvoke執行的委託還會線上程池裡繼續執行,也就是還是有可能把線程池耗盡,我的建議是對於不瞭解的第三方方法,或者已知會阻塞的方法,不要讓線程池去調用它。線程池適合處理那種快速返回的方法。
再有一個人們就說了,我自己實現一套線程池,線程池裡執行一個方法逾時後,我就調用Thread.Abort來終止這個線程,呵呵,想的倒挺好,.net裡如果有個線程調用了非託管的代碼,如果你Abort了這個線程,這個線程不會立刻拋出ThreadAbort異常,而會等待Unmanaged 程式碼返回Managed 程式碼才會拋出異常,那你還是沒解決問題。像這種情況很多,就說常用的Socket.BeginConnect吧,雖說是非同步,可也是有可能阻塞個幾十秒的,其實大多時間在DNS解析上。像這種問題,基本上沒解,除非你自己去重寫Windows的Socket實現去吧,貌似用c++寫的Socket.connect函數也不好控制逾時參數,也是用訊息迴圈或者多個線程來實現,說是有個註冊表索引值,貌似也不怎麼管用。
總結:很鬱悶,沒找到答案,也許我把問題想複雜了。
參考連結:http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!1586.entry
強烈建議dudu自動把http開頭的文字加上超連結,每次我還得自己加。