2011-09-11 82 views
3

在這種特殊情況下,我正在編寫一個TCP/IP服務器,並使用TcpListener.BeginAcceptTcpClient()來接受傳入連接。服務器邏輯在實現IDisposable接口的單個​​類中實現:當調用Dispose()時,我想取消BeginAcceptTcpClient()操作。.NET取消異步I/O操作

現在更籠統地說:當使用.NET(Begin *和End *)的異步I/O模型時,如何取消操作?

我的想法是這樣的:

private volatile bool canceledFlag = false; 

this.listener.BeginAcceptTcpClient(asyncResult => 
{ 
    if(this.canceledFlag) return; 

    // ... 
}, null); 

的Dispose()將標誌設置爲true,也稱之爲this.listener.Stop()。 但是我記得已經讀過,對於每個Begin *調用都必須有一個匹配的End *調用,否則會發生不好的事情。

那麼我應該怎麼做呢?請注意,我正在尋找通用解決方案用Begin *和End *方法取消 - 我只是給了我具體的使用場景,以幫助您理解我的問題。

回答

6

一般解決方法是對您調用的其BeginXxxx()方法的任何對象調用Close()或Dispose()。這會導致回調運行,當你調用EndXxxx()時,你會得到一個ObjectDisposedException。你應該把它當作'使用結束'信號來處理,清理並立即退出回調。雖然皺眉,但流量控制的例外是不可避免的。

對於TcpListener,請使用Server.Dispose(),這比調用Stop()更高效。

+0

啊哈!非常感謝你!請您詳細介紹一下Dispose()和Close() - 有什麼區別? – ShdNx

+1

沒有區別,Close()總是調用Dispose()。在.NET框架設計會議上,有一些痛苦和痛苦。 –

+0

對不起,我的意思是TcpListener.Stop()vs TcpListener.Dispose()。看起來Dispose()是顯式實現的,所以我認爲這是因爲用戶主要是使用Stop()。那麼這裏有什麼不同? – ShdNx

1

我會建議的替代方法不是使用async-Begin-End模式,而是使用任務。

您可以使用TaskFactory.FromAsync重載之一從您的開始/結束方法創建任務並使用它。您可以使用這個.ContinueWith重載指定一個繼續,並使用您自己的CancellationToken來停止它。恕我直言,建議您儘可能使用任務,因爲下一版本的C#將基於異步/等待支持的任務構建。

這裏是一個不錯的article解釋常見的模式和任務

備註:

在我原來的答案,我說你可以使用.Dispose殺任務,但是這可能會導致嚴重的問題,因爲你可以只處理已完成狀態中的任務。

+0

有趣的是,我沒有想過這件事。你當然有一個觀點。謝謝! – ShdNx

+0

請注意我的評論 - 如果它沒有完成處置可能是一個問題.. – Carsten

+0

@CarstenKönig:據我所知,這並沒有幫助原來的問題; 'FromAsync'創建Task1,'ContinueWith'創建Task1完成後執行的Task2。取消Task2不取消Task1或我錯了嗎? – Korexio

1

AsyncCallback在異步過程完成時調用。它允許異步線程重新加入原始程序,並且任何異常都可以起泡(而不是在後臺丟失)。它也允許你捕獲任何返回值。

通常你會使用開始/結束模式如下:

Action action =() = > DoSomething(); 
    action.BeginInvoke(action.EndInvoke, null); 

或者

Action action =() = > DoSomething(); 
action.BeginInvoke(OnComplete, null); 

private void OnComplete(IAsyncResult ar) 
{ 
    AsyncResult result = (AsyncResult)ar; 
    var caller = (Action)result.AsyncDelegate; 
    caller.EndInvoke(); 

    // do something else when completed 
} 

這取決於具體的實現提供一種方式完成之前取消一個異步過程。通常這是通過CancelAsync實現來完成的,該實現可以在早期內部停止任務。在TcpListener的情況下,您可以撥打listener.Server.Dispose();

TcpListener listener = new TcpListener(port); 
IAsyncResult = listener.BeginAcceptTcpClient(listener.EndAcceptTcpClient, null); 

// on dispose 
listener.Server.Dispose(); 
+0

+1對於具體實現來說,需要提供一種在完成之前取消異步進程的方法._恕我直言,這是取消異步操作的唯一干淨方式。 – Korexio