2011-04-25 81 views
6

我的理解是,Thread.Abort應該在阻塞的線程上引發一個ThreadAbortException,但是在處理TcpListener.AcceptSocket時似乎不是這種情況。下面是問題的最基本的例證:AcceptSocket不尊重一個Thread.Abort請求

class Program 
{ 
    static void Main(string[] args) 
    { 
     Thread thread = new Thread(Listen); 
     thread.Start(); 
     Thread.Sleep(1000); // give it a second to get going 
     Console.WriteLine("Aborting listener thread"); 
     thread.Abort(); 
     thread.Join(); 
     Console.WriteLine("Listener thread finished press <enter> to end app."); 
     Console.ReadLine(); 
    } 
    static void Listen() 
    { 
     try 
     { 
      Console.WriteLine("Starting to listen"); 
      TcpListener listener = new TcpListener(IPAddress.Any, 4070); 
      listener.Start(); 
      Socket socket = listener.AcceptSocket(); 
      Console.WriteLine("Connected!"); 
      return; 
     } 
     catch (ThreadAbortException exception) 
     { 
      Console.WriteLine("Abort requested"); 
     } 
    } 
} 

thread.Abort()調用應該停止AcceptSocket並執行ThreadAbortException處理程序。這不會發生。

換出的AcceptSocket爲ListenAsync我聽包裝這就要求BeginAcceptSocket代替:

static void ListenAsync() 
    { 
     try 
     { 
      ManualResetEvent clientConnected = new ManualResetEvent(false); 

      Console.WriteLine("Starting to listen"); 
      TcpListener listener = new TcpListener(IPAddress.Any, 4070); 
      listener.Start(); 
      clientConnected.Reset(); 
      var iasyncResult = listener.BeginAcceptSocket((ar) => 
      { 
       Socket socket = listener.EndAcceptSocket(ar); 
       Console.WriteLine("Connected!"); 
       clientConnected.Set(); 
      }, null); 
      clientConnected.WaitOne(); 

      return; 
     } 
     catch (ThreadAbortException exception) 
     { 
      Console.WriteLine("Abort requested"); 
     } 
    } 

在「顯示」,它這種情況下很好地工作。 ThreadAbortException在線程正在執行WaitOne時被捕獲。但是,這種被調用BeginAcceptSocket創建線程仍在運行,並且能夠接受一個插座

最後(我打開使用Telnet端口驗證了這一點),我添加Listener.StopTheadAbortException處理程序的一部分,並一試捕捉EndAcceptSocket調用(因爲套接字由Stop​​來處理)

這真的是啓動和停止偵聽套接字連接的最佳方法嗎?

+0

當你終止線程時clientConnected事件會發生什麼?你爲什麼不處理物體?你對插座一旦接受了怎麼辦? – 2011-04-25 12:26:16

+0

使用'Thread.Abort'幾乎總是一個骯髒的黑客應該避免。讓你的線程優雅地終止...不要從他們下面拉地毯。 – spender 2011-04-25 12:56:14

回答

7

Thread.Abort不會中止正在套接字上偵聽的線程的原因是因爲該線程被內部阻塞(在道義上相當於)listen()內核調用。當CLR正在執行時,只能由CLR提升ThreadAbortException,並且在調用listen時,線程實際上被卡在非管理系統調用中。一旦對listen的調用返回並且執行在CLR內繼續執行,則線程中止可以繼續。

我第二個建議不要使用Thread.Abort()。如果你決定走這條路,你可以使用與第二個例子類似的技術。但是,如果您想關閉線程偵聽器,最好使用其他方法,並簡單地調用Listener.Stop()

+1

我不確定Thead.Abort()有什麼問題,但是我很樂意聽取建議,並會調查其他選項。 – 2011-04-25 13:01:01

+3

@Ralph:從廣義的角度來看,'Thread.Abort'具有以下問題:a)(正如你在這裏看到的那樣),它給出了錯誤的感覺,即目標線程會立即(甚至*即將*)被中止,但是CLR受到非託管代碼的支配,並且b)它具有破壞性,並且不知道線程在中止時將處於什麼狀態。強烈建議您使用某種取消/中止標誌,並儘可能使用超時阻塞方法。通過這種方式,您可以檢查您的取消標誌並執行當時適當的清理(如果有)。 – 2011-04-25 13:05:13