前幾天作項目的時候須要用到window服務,研究一段時間,算是掌握了最基本的使用方法吧,現總結以下:數據庫
引言:在項目過程當中碰到一個問題:須要不斷的掃描一個大型數據庫表,並獲取dataset,以便作後續的複雜的邏輯處理。若是直接掃描獲取並作邏輯處理,勢必會有很大的性能負耗。如今使用windows服務掃描其變化而不獲取dataset,只有當windows服務告訴我能夠獲取dataset時再進行獲取,並作邏輯處理,大大提升了其性能。編程
windows服務應用程序是一個沒有前臺界面的應用程序,同時其佔用內存資源較小便於長期運行,所以被衆多編程者使用,多用於服務器環境下。因爲其沒有用戶界面所以不會產生可視輸出,而且能夠對其啓動和中止進行按需設定,很是方便。c#
1、建立windows服務:windows
平臺:vs2010緩存
工具:c#服務器
功能實現:向一個txt文件寫入當前時間。(徹底是學習練習使用,全部沒有涉及到特殊的功能)框架
首先在vs2010中建立一個windows服務工程,默認有一個service1.cs文件,打開此文件,建立一個計時器,每隔1秒鐘掃描一次。在計時器的t1_Elapsed中添加編寫日誌的方法(writelog)。異步
Onstart控制服務啓動;Onstop:控制服務中止。ide
在OnStart中加入計時器啓動的方法,OnStop中加入計時器中止的方法。至此便完成一個簡單的Windows服務的建立。工具
參考代碼:
System.Timers.Timer t1 = new System.Timers.Timer(); public Service1() { InitializeComponent(); t1.Interval = 1000; t1.Elapsed += new System.Timers.ElapsedEventHandler(t1_Elapsed); } void t1_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { Writelog(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")); } protected override void OnStart(string[] args) { t1.Start(); } protected override void OnStop() { t1.Stop(); }
2、安裝、發佈windows服務:
將service1.cs的視圖設計器打開,右鍵點擊添加按照程序,顯現serviceProcessInstaller1和serviceInstaller1兩個組件,點擊serviceProcessInstaller1的屬性,將其改成LocalService,點擊serviceInstaller1的屬性,能夠在serviceName中更改服務的名字,在startType中更改服務的啓動方式。最後生成解決方案。
使用InstallUtil.exe工具,安裝生成的應用程序。提示:在解決方案生成的項目應用程序的目錄下進行安裝,能夠debug,也能夠是release。
在安裝過程當中可使用批命令來實現:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil -u FirstService.exe
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil FirstService.exe
其中前面是InstallUtil.exe的存放位置,能夠根據不一樣的版本查找其確切位置。FirstService是應用程序,能夠替換爲項目生成的應用程序。
注:在安裝過程當中若是一切順利固然是幸事,本人的安裝足是用了兩個下午,最後在一大牛的指導下才安裝正確。究其緣由,檢查你使用的計算機的用戶是否對你項目所在位置的盤是否賦予其相應的權利(管理員)。(深受其苦)
3、調試windows服務:
一、將建立的windows服務發佈到本地系統中。
二、打開vs2010,在項目源代碼中設置斷點。
三、點擊調試按鈕,打開附件到進程對話框,選擇所須要進程後點擊附加按鈕。
四、啓動服務便可進行調試。
windows服務成功完成後,本來是想在不一樣的進程間創建通訊,最後通過詢問先驅者,本項目的需求是當數據有改變時將其變化保持到另外一數據庫表便可,完成目標。可是這裏勾起了我學習進程間通訊的好奇心。
進程間通訊方式:
匿名管道:半雙工通訊方式,數據只能在有親緣關係的進程間單向流動。通俗來說:用來在父進程和子進程間傳輸數據,而且是在本機上使用。
命名管道:相對應匿名管道來講的,單向或雙向的,用在服務端和一個或多個客戶端進行通信。一個命名管道的全部實例共享一樣的管道名,可是每一個實例都有各自的緩存和句柄,做爲一個隔離的通道,讓客戶端-服務器端進行通信,這些實例容許客戶端同時使用相同名字的管道。
信號量:信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問。它常做爲一種鎖機制,防止某進程正在訪問共享資源時,其餘進程也訪問該資源。所以,主要做爲進程間以及同一進程內不一樣線程之間的同步手段。
消息隊列:消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點
信號:信號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生。
共享內存:共享內存就是映射一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問。共享內存是最快的 IPC 方式,它是針對其餘進程間通訊方式運行效率低而專門設計的。它每每與其餘通訊機制,如信號兩,配合使用,來實現進程間的同步和通訊。
套接字:平常工做中常常遇到,這次不作介紹。
方式有不少,目前爲止只用到過套接字,在時間有限的狀況下本身僅選擇命名管道進行學習。
命名管道,上面也介紹了不少,它是相對於匿名管道來講的。因爲是初次學習,是學習而不是爲了完成項目需求,因此本着實現最基本的框架便可的原則來進行下面講述。
命名管道有兩個很重要的類:
NamedPipeClientStream:公開命名管道周圍的 System.IO.Stream,該管道既支持同步讀寫操做,也支持異步讀寫操做。
NamedPipeServerStream:公開命名管道周圍的 System.IO.Stream,該管道既支持同步讀寫操做,也支持異步讀寫操做。
從上面的定義也可看出client和server定義相同,所以其通訊角色也可互換。同時其定義也正對應着其字面意思。有點拗口,簡單來講無非就是定義有名字的管道周圍的stream。多餘的不說了
參考代碼:
Server端:
Thread receiveDataThread = new Thread(new ThreadStart(ReceiveDataFromClient)); private static void ReceiveDataFromClient() { while (true) { try { NamedPipeServerStream pipeServer = new NamedPipeServerStream("closePipe", PipeDirection.InOut, 2); pipeServer.WaitForConnection(); StreamReader sr = new StreamReader(pipeServer); string receiveData = sr.ReadLine(); Thread.Sleep(1000); sr.Close(); Console.WriteLine("[server] receive {0} from client", receiveData); } catch (Exception ex) { throw new Exception(ex.Message); } } }
client端:
private static void SendDataToServer() { try { NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "closePipe", PipeDirection.InOut, PipeOptions.Asynchronous); pipeClient.Connect(); StreamWriter sw = new StreamWriter(pipeClient); sw.WriteLine("hello,everyone"); sw.Flush(); Thread.Sleep(1000); sw.Close(); Console.WriteLine("[client] send hello,everyone to server"); } catch (Exception ex) { throw new Exception(ex.Message); } }