2015-06-07 23 views
8

在過去的幾個月中,我一直在閱讀關於C#中的異步等待以及如何正確使用它。如何在新線程上運行任務並立即返回給調用者?

爲了實驗練習的目的,我正在構建一個小型的Tcp服務器,它應該爲連接它的客戶端提供服務。該程序是一個控制檯應用程序。

我使用while循環來等待連接,像這樣:

while (!_isStopRequested) 
{ 
     TcpClient client = await _tcpListener.AcceptTcpClientAsync(); 

     await Task.Factory.StartNew(() => ProcessClientAsync(client), TaskCreationOptions.LongRunning); 
} 

所以,到現在爲止我做的方法ProcessClientAsync標記爲異步作廢,我只是把它ProcessClientAsync(客戶端)和呼叫會立即返回給調用者。 但是我在網上看到,除非是事件,否則使用它是一個糟糕的決定。 所以我將定義更改爲異步任務。

好的,但沒有等待,我在Visual Studio中收到警告「因爲這個調用沒有被等待,當前的方法在調用完成之前繼續運行」。

一旦我使用了ProcessClientAsync(客戶端),代碼就無法工作。我只能連接一個客戶端,然後調用者「等待」ProcessClientAsync返回。但是,該方法具有無限循環並且不會返回,但我需要while循環來繼續處理下一個客戶端。

谷歌搜索,我想出了這個主題: How to safely call an async method in C# without await

我猜問題是大同小異的,只是當我使用等待ProcessClientAsync,我希望它立即返回給調用者,它沒有按」我知道,因爲運行第二個客戶端,而第一個仍在運行,第二個客戶端不連接。

所以我這樣做:

await Task.Factory.StartNew(() => ProcessClientAsync(client), TaskCreationOptions.LongRunning); 

但由於ProcessClientAsync必須返回一個任務,我不知道這是否是好的呢?

這將是一個問題。

另一個問題是:我怎樣才能調用一個永久運行的異步Task方法,並立即將調用返回給調用者,這樣while循環可以繼續,並且tcp偵聽器可以繼續接受下一個客戶端?

謝謝。

對不起,如果是重複,或不清楚我在問什麼。

+0

請縮小你的問題:它過於寬泛。另外,爲了更好的可讀性,做一些編輯,排除非必要部分。感謝和問候, –

回答

10

爲了進行實驗室練習,我正在構建一個小型Tcp服務器,該服務器應該爲連接到它的客戶端提供服務。該程序是一個控制檯應用程序。

那麼,我建議的第一件事就是嘗試一個更簡單的練習。嚴重的是,一個異步TCP服務器是您可以選擇的最複雜的應用程序之一。絕大多數使用async的開發者正在開發應用程序兩個數量級更簡單。

除此之外,請嘗試編寫一個將從URL下載文件的UI應用程序。這對於學習async來說是一個更現實的起點。

但是由於ProcessClientAsync必須返回一個任務,我不確定這是否可以做?

我推薦使用Task.Run而不是Task.Factory.StartNewTask.Run知道async方法,而StartNew不是。

但是,這是假設開始一個線程是正確的操作。大多數TCP/IP服務器應用程序不受CPU限制,所以我在這裏質疑使用多個線程。完全可以有一個完全異步的TCP/IP服務器,它只爲其async方法的延續使用線程池。

另一個是:我怎麼能調用,將永遠運行的異步任務的方法,並有調用返回給調用者立刻讓while循環能夠繼續和TCP監聽器可以繼續接受下一個客戶端?

你不awaitTask返回:

Task.Run(() => ProcessClientAsync(client)); 

然而,編譯器會在這裏,因爲這段代碼幾乎可以肯定是錯誤的。如果您忽略返回的任務,那麼您將忽略該代碼中的任何異常。

TCP/IP服務器應用程序中的常見模式是爲每個連接的客戶端維護一個小集合狀態。這是把兩個socket對象本身並表示套接字連接的處理任務自然的地方:

var clientState = new ClientState(client); 
clientState.Task = Task.Run(() => ProcessClientAsync(client)); 

這些客戶端的狀態,然後存儲在列表/字典。你如何做到這一點當然取決於你。

+0

謝謝你的全面答案。我嘗試了您的建議並創建了一個接受tcp客戶端的ClientState類。然後我做了clientState.Task = Task.Run(()=> ProcessClientAsync(client));如你所建議的那樣,但它的行爲與我等待時的行爲一樣。我把這段代碼放在while循環中。 – Marin

+0

@Marin:如果你不'等待'Task'(也不要阻塞它),那麼主線程將繼續執行。 –

+0

謝謝你,你是對的,錯誤是在別的地方!一個非常奇怪的錯誤...我決定把客戶端狀態放入併發包中,並添加Add方法,但忘記實例化它......奇怪的是,我沒有得到空引用異常?!這怎麼可能?! 總而言之,它現在的作品...謝謝! – Marin

1

這將啓動任務並返回給調用者,並避免編譯器警告:

var t = Task.Factory.StartNew(() => {}); 
0

如果我正確理解你的問題,那麼此代碼應幫助:

private async void AcceptClient(TcpClient client) 
{ 
    await Task.Factory.StartNew(() => ProcessClientAsync(client), TaskCreationOptions.LongRunning); 
} 

private async void Run() 
{ 
    while (!_isStopRequested) 
    { 
     TcpClient client = await _tcpListener.AcceptTcpClientAsync(); 
     AcceptClient(client); 
    } 
} 
+0

OP從無效切換到任務指導,但我認爲這種情況符合異步無效方法的指導方針就好了。 – glenebob

+0

它確實跨過我的想法,也許它可以使用異步無效,但我想使它「通過本書」,因此更改爲異步任務 – Marin

相關問題