2013-06-19 102 views
-2

我寫了一個服務,該服務運行一個線程,每分鐘設置一次設置。命名管道在服務中導致CPU使用率高

該服務做它應該做的事情,但後來發現它使CPU使用率變得非常高(在雙核上約爲25%)。

使用試驗和錯誤,我發現以下對象正在導致問題:

private AsyncPipes.NamedPipeStreamServer pipeServer = new NamedPipeStreamServer("NotifyToService"); 
private AsyncPipes.NamedPipeStreamClient pipeClient = new NamedPipeStreamClient("ServiceToNotify"); 

這是正常的命名管道使用如此多的CPU,只是被實例化?

+8

可能需要顯示您的代碼。 –

+1

您可能想要使用睡眠間隔。看到這個例子: http://stackoverflow.com/questions/17150856/windows-service-not-executing-after-first-run/17151266#17151266 – jor

+0

你有沒有觀察到如果你運行另一個CPU密集型的程序會發生什麼好? – rro

回答

5

我可以複製你的結果(除了13%我的8核CPU) 。我必須從this article的末尾下載並構建AsyncPipes庫。問題在於NamedPipeStreamClient中的代碼每秒拋出一次System.TimeoutException

在設計不好的情況下,NamedPipeStreamClient的構造函數在設置了一些類成員之後調用了一個名爲StartTryConnect()的方法。

該方法反過來啓動後臺線程,調用方法TryConnect。它會進入這裏緊密循環:

while (!this._Stream.IsConnected) 
{ 
    try 
    { 
     ((NamedPipeClientStream) this._Stream).Connect(0x3e8); 
    } 
    catch 
    { 
    } 
} 

直到您的客戶端試圖連接到(「ServiceToNotify」)的服務器實際啓動,這將是這種情況。然而,我沒有看到任何地方你有一個名爲管道服務器的命名管道服務器(你有相反的,「NotifyToService」)啓動。

但是,一旦它連接到服務器,CPU使用率將按預期下降。

+0

你是對的。解決方法是更改​​代碼,以便只在預期在另一端有監聽器時才嘗試連接。由於這是代理應用程序的服務,所以代理應用程序將僅在用戶登錄後才啓動。或者要增加超時持續時間。 –

+2

我在'catch'塊中添加了一個'Thread.sleep(20000)'。問題解決了。 –

+3

對NamedPipeClientStream.Connect()方法的愚蠢設計感到驚訝 –

-2

除非服務在某個時刻睡覺/喚醒,否則這是一種非常正常的行爲。默認情況下,while true{}循環將使用其執行位置的100%的處理能力。 25%聽起來很像電腦上可用的4個線程中的1/4。

你真的想使用100%的CPU,只要你的代碼,否則你爲什麼要支付速度更快的計算機?......

+1

這是一個相當愚蠢的答案。 「當你編碼時,你實際上想要使用100%的CPU ......」不,我不想在後臺運行的通信功能中使用100%的CPU - 這完全破壞了我的UI的響應能力。我特別不希望它是由於框架中的一個錯誤而引起的,這顯然就是這種情況。 – RenniePet

1

這是我最終用來避免這個問題。正是在這個MSDN論壇話題中所示的解決方案的一個非常簡化的版本:

https://social.msdn.microsoft.com/Forums/en-US/7bbf5a0b-3c22-4836-b271-999e514c321b/namedpipeclientstreamconnect-causes-cpu-hang-is-there-a-workaround

[return: MarshalAs(UnmanagedType.Bool)] 
    [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] 
    private static extern bool WaitNamedPipe(string name, int timeout); 


    /// <summary> 
    /// Method to test if Windows considers that a named pipe of a certain name exists or not. 
    /// </summary> 
    internal static bool DoesNamedPipeExist(string pipeFileName) 
    { 
    try 
    { 
     return WaitNamedPipe(@"\\.\pipe\" + pipeFileName, 0); 
    } 
    catch (Exception) 
    { 
     return false; 
    } 
    } 

這是其中使用NamedPipeClientStream.Connect()方法的代碼周圍的位置的位:

// If necessary, wait for the server end to open the named pipe. Otherwise the following 
// NamedPipeClientStream.Connect() method can burn up 100% CPU time while waiting for 
// the connection. 
while (true) 
{ 
    if (DoesNamedPipeExist(_pipeFileName)) 
     break; 
    Thread.Sleep(1000); 
} 

// Connect to named pipe server - this is a blocking call, and in fact is a blocking 
// call that can use 100% CPU time while waiting for a connection 
_namedPipeClient.Connect(); 
+0

它令讀者在閱讀參考資源上的管道代碼時感到驚奇。這是一個很好的解決方法。謝謝。 –