解讀 .NET開發者常犯的錯誤_.Net教程

      編輯Tag賺U幣
      教程Tag:暫無Tag,歡迎添加,賺取U幣!

      推薦:談對程序開發中異常的處理
      從接觸異常開始我就弄不明白她,不會用她,想在系統中是異常機制發揮的淋漓盡致,進行了很多嘗試,利用異常控制程序流程,利用異常做數字的判斷函數,利用異常消除系統中可能出現的惱人的異常提示框,為了更好了利用異常看了很多關于異常的文章,直到有一天

      .NET開發過程中不是程式的無法運行,就是程式的效率慢的同蝸牛在爬; 這種情況在.NET的新手中尤其常見;我不知道為什么,一些介紹.NET開發的書本里引用的例子代碼,也對此問題視而不見,尤其讓我郁悶的是一些我喜歡的書也出現了同樣的問題。

      這篇文章不僅對.NET開發者的新手有幫助,同樣對哪些有經驗,也帶來一些啟示和參考。

      他們會遇到什么樣的問題?

      1. 數據庫連接超時

      2. 創建的對象只管用,不管釋放

      3. 調試(Debug)模式下編譯后,就用于應用環境中了

      4. 實際作業模式分享

      上面的問題就像毒瘤,積累到一定程度就爆發,且影響深遠。

      1.數據庫連接超時篇

      若要知道數據庫連接超時問題,先看下面一段代碼:

      [sample-01]

      以下為引用的內容:

      Public Shared Function getOEMPN(ByVal psPN As String, ByRef OEMPN As String) As BSResult
      Dim clsResult As New BSResult
      Try
      clsResult.ResultID
      = -1
      Dim dtResult As New DataTable
      Dim Sql As String
      = String.Empty
      Dim clsOraDb As New clsOraClienDb
      Dim strConn As String
      = ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
      clsOraDb.Open(strConn)  ‘這里Open后,后面看不到 clsOraDb.Close
      Sql
      = "SELECT  SATBMMBRND.OEMPN FRUNO FROM SATBMMBRND  WHERE SATBMMBRND.MATNO = :MATNO"
      Dim
      params() As racleParameter = {New OracleParameter("MATNO", psPN)}
      If clsOraDb.FillDataTable(Sql, dtResult,
      params) = False Then
      Return clsResult
      End If
      If dtResult Is Nothing Then
      Return clsResult
      End If
      If dtResult.Rows.Count
      > 0 Then
      OEMPN
      = dtResult.Rows(0)("FRUNO").ToString()
      Else
      OEMPN
      = ""
      End If
      clsResult.ResultID
      = 1
      Return clsResult
      Catch ex As Exception
      clsResult.ResultID
      = -1
      Return clsResult
      End Try
      End Function

      對上述代碼行的部分解釋:

      Dim clsOraDb As New clsOraClienDb:引用數據庫連接的類;

      clsOraDb.Open(strConn):打開數據庫連接;

      然后,整個函數你再找不到關閉數據庫連接的動作,是要等著操作系統來釋放嗎? 有人就說啦,看起來好像沒有什么大不了的,這僅僅是一個函數而已;數據庫打開連接,未關閉不會影響到整個應用程式;果真是這樣嗎?

      讓我們談談數據庫連接的問題,在Oracle數據庫里,一般默認的數據庫連接數最多也就100多來個,不會超過200個,即使你改變這個連接數;但無論怎樣,它的連接數是有限的;不可能無限地供你消耗。

      在Web這個程式里,它不僅不會自動關閉數據庫連接,象這樣的函數還會每次調用,都會重新用掉一個數據庫連接;如果象這樣的函數很多的話,你就等著一個錯誤警告頁面彈出來,如Database Connection Timeout…等訊息。

      這還不算什么,更有甚者,盡然在循環語句里寫下面的代碼如 :

      [sample-02]

      以下為引用的內容:

      Foreach(DataRow row in tabl.select(“”,”ProductID”)
      ……………
      clsOraDb.Open(strConn)
      ………….
      Next
      有人還喜歡玩下面的語句:
      [sample
      -03]
      Foreach(DataRow row
      in tabl.select(“”,”ProductID”)
      Foreach(DataColumn col
      in tbl.columns)
      ……………
      clsOraDb.Open(strConn)

      Next
      ………….
      Next

      說到這,有人就問啦;我在開發環境下測試一點問題都沒有呀?是呀,你是沒有問題;我想問的是,你開發環境的測試數據有幾筆?

      現在,問題已經知道在哪里,怎么解決?

      針對[sample-01]做如下處理,注意下面代碼:

      以下為引用的內容:

      Public Shared Function getOEMPN(ByVal psPN As String, ByRef OEMPN As String) As BSResult
      Dim clsResult As New BSResult
      Try
      clsResult.ResultID = -1
      Dim dtResult As New DataTable
      Dim Sql As String
      = String.Empty
      Dim clsOraDb As New clsOraClienDb
      Dim strConn As String
      =
      ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
      clsOraDb.Open(strConn)  注釋:這里Open后,后面看不到 clsOraDb.Close
      Sql
      = "SELECT  SATBMMBRND.OEMPN FRUNO FROM SATBMMBRND  WHERE SATBMMBRND.MATNO = :MATNO"
      Dim
      params() As racleParameter = {New OracleParameter("MATNO", psPN)}
      If clsOraDb.FillDataTable(Sql, dtResult,
      params) = False Then
      Return clsResult
      End If
      If dtResult Is Nothing Then
      Return clsResult
      End If
      If dtResult.Rows.Count
      > 0 Then
      OEMPN
      = dtResult.Rows(0)("FRUNO").ToString()
      Else
      OEMPN
      = ""
      End If
      clsResult.ResultID
      = 1
      clsOraDb.Close  注釋:后面看到 clsOraDb.Close
      Return clsResult
      Catch ex As Exception
      clsOraDb.Close 注釋:程序異常也看到 clsOraDb.Close
      clsResult.ResultID
      = -1
      Return clsResult
      Throw ex
      End Try
      End Function

      注意上面的兩句代碼:clsOraDb.Close和clsOraDb.Close;

      在異常處理的時候,特別提醒兩點:

      (1)你的數據庫關閉的時候應該是在代碼行0028前,而不是后;

      (2)有人不習慣(或者一時疏忽)加上0088行的代碼;

      針對[Sample-02]和[sample-03],把打開數據庫連接寫在所有的循環語句之前,如:

      以下為引用的內容:

      clsOraDb.Open(strConn)
      Foreach(DataRow row in tabl.select(“”,”ProductID”)
      ……………

      ………….
      Next 

      當然還有另外一個做法,就是用Using語句,提交.NET應用的垃圾收集器自動收集;相關的文章很多;這里不再特別贅述。

      2.對象只管創建應用,不管釋放篇

      我們繼續用[Sample-01]的代碼:

      Dim dtResult As New DataTable

      誰會發現它被釋放,你不能,我也不能,從來沒有被釋放過。

      “Dim dtResult As New DataTable”行的代碼解釋是,要在內存劃分一個空間給這個定義的對象dtresult;系統要劃分多大的空間呢?呀,我沒有研究過(留給那些有心人吧,呵呵。.);但有一點,要在內存劃分一個空間,就是要占用內存;那么內存有多大呢,不是無限大吧;也是有限的;所有運行上述代碼的最終結果是,系統的執行效率越來越慢;有人就懷疑,我有內存1到2G的,加上虛擬內存就更大;我只能說你的懷疑沒錯;可是你的應用程序就用這么一只函數嗎?我想肯定不是;所以上百只函數的應用執行對內存的消耗可想而知;如果是后臺自動運行的程序,及時是一個function,也會讓系統崩潰;這只是一個簡單的例子,有更復雜的;象這樣的對象應用還有:Dataset, Datatable,DataReader,DataAdapter,Datagrid.。等;

      那么怎么解決這些問題呢?

      (1)在Try catch 語句前定義好所用的對象; 如:

      以下為引用的內容:

      Dim dtResult As New DataTable
      Dim DR
      as New DataReader
      Dim DS
      as New Dataset
      Try
      ..
      Catch ex As Exception

      (2)釋放的語句如下:

      以下為引用的內容:

      Dim dtResult As New DataTable
      Dim DR
      as New DataReader
      Dim DS
      as New Dataset
      Try
      ..
      ……………..
      Catch ex As Exception
      --釋放應用的對象
      Throw ex
      Finally
      --使用完后,釋放應用的對象
      dtResult.dispose  
      --從內存里清楚該

       有人習慣寫成下面這樣:

      以下為引用的內容:

      Dim dtResult As New DataTable
      Dim DR
      as New DataReader
      Dim DS
      as New Dataset
      Try
      ..
      ‘使用完后,釋放應用的對象
      dtResult.dispose  ‘從內存里清楚該對象
      DR.dispose    ‘從內存里清楚該對象
      DS.dispose    ‘從內存里清楚該對象
      Catch ex As Exception

      這不是也釋放了嗎?我想問的是,如果程序出現異常,它們會釋放嗎?我肯定得告訴大家,它們一定不能釋放。為了確保程序的穩定運行,我建議大家都來用Try Catch語句。

      (3)絕不建議在循環語句寫如下的語句:

      以下為引用的內容:

      Foreach (DataRow row in tabl.select(“”,”ProductID”)
      ……………
      Dim DS
      new Dataset 記住此乃寫代碼之大忌;
      Dim DT
      new Databable….
      ………….
      Next

      還有一種寫法
      Dim DS
      new Dataset
      Dim DT
      new Databable…
      Foreach (DataRow row
      in tabl.select(“”,”ProductID”)

       正確的寫法是:

      以下為引用的內容:

      Dim DS new Dataset
      Dim DT new Databable…
      Try
      Foreach (DataRow row in tabl.select(“”,”ProductID”)
      DS=nothing  ‘每次使用,都先把內存空間釋放出來
      DT=nothing  ‘每次使用,都先把內存空間釋放出來
      DS=GetDatase
      DT=GetDatatable……………
      .
      ………….
      Next
      Catch ex As Exception
      Throw ex
      Finally
      DS.dispose
      DT.dispose
      End Try

      另外,提醒大家一點,記得用 For Each 語句替代For i=0 to Rowcount-1;這樣的效率改善也是明顯的;

      3.調試(Debug)模式下編譯就用于應用環境中篇

      大家看下面的圖片:

      有人會留意這個界面嗎?有,但一定不多。

      接著,程式開發好(也包括單元測試),然后編譯直接分發到應用環境。

      整個過程就結束了;誰也不曾想,這里埋下了一個深深的地雷;據微軟的人講,這樣分發的程式到應用環境,你有多少內存恐怕都不夠,所以微軟建議我們做如下的工作:

      “請將Web.config中的debug及Trace均設為False。還有您的所有程式請確保compile為Release Mode

      Application set up for debugging

      One reason for high memory that we see here in Support a lot is when you have debugging, tracing, or both enabled for your application.

      While you are developing your application, this is a necessity.By default, when you create your application in Visual Studio .NET, you will see the following attribute set in your Web.config file:

      and/or

      Also, when you do a final build of your application, make sure that you do this in “Release” mode, not “Debug” mode. ”

      如果不這樣做會有什么事情發生? 我分享一個同事的感受給大家:“已經是很強悍的的DB和AP服務器(全部是刀片式服務器)了,可是問題一而再再而三地發生,那種感覺真的很無助很凄涼啊~”

      后來的結果發現是內存使用率超高,到一定限度的時候,就會反應變慢,這個時候只要重啟IIS就可以好一段時間;后來分析IIS用到實體加虛擬的內存超過2G就會爆掉;

      這就是原因,你想遇到嗎?那就不妨試試。

      4.實際操作分享篇

      上面三個環節,任何一個發生問題,都會影響到系統的效率;我分享我們實際的作業的過程發生的一些情況,及怎么解決這些問題。

      (1)內存使用達到峰值,導致程序無法繼續運行;

      有個同事分享了他們的經驗如下(原話分享):

      我們有一些程式是server跑的Job,并有越來越多之勢。而大家在寫程式的時候可能比較少考慮到耗內存這個問題。

      下面的例子也許會給我們一點啟示。

      下面也是原話:

      Pls help to check the Run In Rack Job program .  It will no response after running two or three days . the AP server Memory usage will over 2.5G . after we close the the program , Memory will decrease to 1.5 .

      大致意思是:在服務器端(也叫后臺)自動跑的一只程式,運行了兩三天后,停止運行了;

      檢查Server的內存使用率時,發現超過了2.5G;在關掉了這只程式后,它就降到了1.5G…下面的圖片為證:

      (2)進程請求過多,導致CPU無法及時處理,程序效率反應較慢。

      下面都是同事的原話:

      “年后產量逐漸增加,新的問題又出現了。從Server Performance上分析,和上次Memory過高不同的是CPU使用率過高。每當CPU過高的時候,產線會大面積的反應說慢(這點和連接到哪臺AP有關系)。每次慢的時候,我們就找到CPU過高的那臺AP,recycle IIS的application pool后就OK了。于是我么再次找到Bon幫忙分析(結論:微軟結案報告 20090226V1 - SRT090119833891 Web service can‘t serve IISReset can fix.msg)。并給出了開發程序時的一些建議。

      結論大致是說,沒有進程占用了特別高的CPU,也沒有進程占用CPU時間過長。只是對DB的請求的進程過多(比較吻合3廠的實際狀況—附件多,刷的快),加起來就整體過高。還發現了很多DLL是built in debug mode,這些DLL占用了過多的memory資源。后來根據Bon的建議,我們修改了IIS application pool的設定如下,解決過多請求不能及時處理,而造成CPU過高的問題。”

      這里有一些問答關于應用連接池(Application Pool)的設定,對理解這樣的設置有一定的幫助:

      1. Is one application pool’s maximum memory usage 1.5G?
      A&: Each application pool is a w3wp.exe. w3wp.exe is a process. Every process has 2 G User mode virtual address, so the maximum memory usage for application pool is 2G. However, you can’t make sure that there is no memory fragment issue. Therefore, Out of memory always occur after 1.5 G according to our experience.

      2. Is each application pool independent on memory usage?
      A&: Different application pools are different w3wp.exe, so each application pool’s maximum memory usage is 2G.

      3. Can setup maximum CPU usage on each application pool?
      A&: You can monitor it, but you can’t setup it.

      分享:解析.Net中圖片的快速處理
      在圖片處理過程中,我們經常需要對圖片逐像素進行處理,比如為了使圖片某一向量的顏色加深或者減淡,或者為了使圖像變化成黑白顏色,這個時候我們需要取出每個點上的像素進行計算,再賦值到圖像指定的位置。在.Net中,官方提供了Image.GetPixel(int x, int

      來源:模板無憂//所屬分類:.Net教程/更新時間:2009-08-25
      相關.Net教程