2014-10-31 44 views
1

我使用這個代碼給一個時間限制,以便另一個線程和一定時間後其中斷:獲取當前行從另一個託管線程

void RunWithTimeout(Action action, TimeSpan timeout) 
    { 
     AutoResetEvent signal = new AutoResetEvent(false); 
     Thread workerThread = null; 
     ThreadPool.QueueUserWorkItem((o) => 
     { 
      workerThread = Thread.CurrentThread; 
      action(); 
      signal.Set(); 
     }); 

     using (new Timer((o) => { signal.Set(); }, null, (int)timeout.TotalMilliseconds, Timeout.Infinite)) 
     { 
      signal.WaitOne(); 
     } 

     if (workerThread != null && workerThread.IsAlive) 
     { 
      try 
      { 
       workerThread.Abort(); 
      } 
      catch { } 
      throw new System.TimeoutException(); 
     } 
    } 

我使用.NET 3.5,所以我不能使用任務 我現在拋出TimeoutException,但我想知道在調用Abort時正在執行的行。

有沒有辦法讓另一個線程調用堆棧並將它傳遞給異常的StackTrace屬性?

+1

嗯,相當不健康的假設實際上有*是*「當前行」當代碼需要中止時。它幾乎肯定被埋在了內部CLR或操作系統功能中。那種讓線程掛起而不能及時完成工作的方式。中止線程是危險的,互聯網上有100萬條關於它的警告。 – 2014-10-31 12:49:12

+1

中止線程充滿錯誤,除非在極端的情況下,否則不應該這樣做。如果你控制了代碼,你應該編寫線程來支持一些協作取消手段。在.NET 4.0中,你可以使用'CancellationToken'。在早期版本中,使用'WaitHandle'。看我的博客文章,[輪詢取消](http://blog.mischel.com/2013/05/07/polling-for-cancellation/) – 2014-10-31 12:50:18

回答

2

您不需要Timer來等待另一個線程,您可以使用接受以毫秒爲單位的超時的WaitOne重載。

爲了捕捉工作線程的堆棧跟蹤,你可以通過一個封閉的可變傳遞ThreadAbortException主線程:

private static void RunWithTimeout(Action action, TimeSpan timeout) 
{ 
    Thread worker = null; 
    var signal = new AutoResetEvent(false); 
    ThreadAbortException abortException = null; 
    ThreadPool.QueueUserWorkItem(o => 
    { 
     worker = Thread.CurrentThread; 
     try 
     { 
      action(); 
      signal.Set(); 
     } 
     catch (ThreadAbortException ex) 
     { 
      //thread is being aborted, pass the exception back to the main thread 
      abortException = ex; 
     } 
    }); 

    if (!signal.WaitOne(timeout)) 
    { 
     worker.Abort(); 
     //abortException is now filled from the worker thread, the stack trace for the 
     //worker thread is now inside the InnerException of the ThreadAbortException 
     throw new TimeoutException("Operation timed out", abortException); 
    } 
} 
+0

是的,'WaitOne'而不是一個計時器。順便說一句,有一個[WaitOne重載](http://msdn.microsoft.com/en-us/library/cc190477(v = vs.110).aspx),它需要一個'TimeSpan'。所以你可以寫'WaitOne(timeout)',而不是先轉換成毫秒。 – 2014-10-31 12:47:30

+0

@JimMischel良好的通話,更新的答案 – Bas 2014-10-31 13:00:19

1

您可以用醒目ThreadAbortException嘗試如下證明:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var thread = new Thread(Work); 
     thread.Start(); 
     Thread.Sleep(3000); 
     thread.Abort(); 
    } 

    static void Work() 
    { 
     try 
     { 
      while (true) 
      { 
       // do work here 
      } 
     } 
     catch (ThreadAbortException ex) 
     { 
      Console.WriteLine(ex.StackTrace); 
      // TODO: pass ex.StackTrace to main thread 
     } 
    } 
} 

堆棧跟蹤將包括在其中,當它被中止線程的方法的名稱。 ThreadAbortException將被自動重新拋出,所以線程仍然中止。