Wednesday, August 1, 2012

[C#] Using FileStream StreamWriter avoid IOException

今天在用JMeter對公司的程式作壓力測試,但是我發現錯誤率一直在0.5~1%,這是相當高的錯誤率,所以就來查啦。

從JMeter紀錄來看,錯誤訊息是這樣
System.IO.IOException: 由於另一個處理序正在使用檔案 'filename',所以無法存取該檔案。
System.IO.IOException: The process cannot access the file 'filename' because it is being used by another process.
原因是在程式中會去對實體檔案寫log,平時看不出錯誤,但是壓力測試下就會因為檔案lock住產生 IOException 。

原本的code長的這樣
public static void WriteLog()
      {
            StreamWriter log;

            if (!File.Exists("FilePath"))
            {
                log = new StreamWriter("FilePath");
            }
            else
            {
                log = File.AppendText("FilePath");
            }

            log.WriteLine("Write Something");
            log.Close();
      }

我先改成用Using StreamWriter,原因可以看這邊using Statement,但是再次測試時,發現錯誤率雖然降低了,可是還是有錯,這是不容許的啊!! 最後一時找不到方法,只好加上lock,然後就再也測不到 IOException 的錯誤,可以算是解決問題了,但是我想搞不好有更好的方法就是。

最後code改成這樣
private static readonly object LockFile = new object();
        public static void writeFile()
        {
            lock(LockFile)
            {
                using (var log = new StreamWriter("FilePath", true))
                {
                    log.WriteLine("Write Something");
                }
            }
        }
題外話,StreamWrite有個StreamWriter Constructor (String, Boolean)可以自動判斷沒檔案就新增,有檔案就加在後面,所以不用自己判斷了。

話說我後來找到一個非同步檔案 I/O,不曉得能不能解這種問題就是....


2012/08/16 update

之前壓力測試時,因為在本機測試,每秒跑不到100次,所以用之前的方式就可以避掉file lock的問題,後來搬到測試機上測試,加上程式效能又調教過,變成每秒可以跑500~600次,這時候又遇到IOException的問題了,剛遇到時,試了一些方法,都還是會有問題,不然就是搞到Out of memory XD ,原本都已經在看非同步存取的方式能不能解了,沒想到昨天晚上打LOL之前,又找了一下幾種可能的解法,今天到公司一試,還真的解掉了這問題。
        private static readonly object LockFile = new object();
        public static void writeFile()
        {
            using (var fs = new FileStream(filePath,FileMode.Append,FileAccess.Write,FileShare.ReadWrite))
            {
                using (var log = new StreamWriter(fs))
                {
                    lock (LockFile)
                    {
                        log.WriteLine(logtxt);
                    }
                }
            }
        }
說穿了也就只是用FileStream設定正確的FileShare權限而已,沒想到這麼簡單就解決了 -_-

在找解法的時候,也試過3rd party 的logging方式,如 NLog、log4net ,但是效能都會被拖到剩下1/3不到,只剩每秒不到200次,後來還是回頭自己寫效率比較好。

Jmeter2

而IOException在目前的測試下是還沒有發生過,速度也不會慢很多,所以這解法暫時是可以work的,等壓力測試能跑到更高的數值的時候,再看看有沒有需要改吧。

1 comment:

  1. 您好,我也在工作上遇到相同問題,明明已經用lock{}鎖住存取檔案的區塊,單依舊會發生
    System.IO.IOException: 由於另一個處理序正在使用檔案 'filename',所以無法存取該檔案。
    的狀況,所以想請教您,為何已經用了lock還是無法避免呢?為何還需要用到你所寫的第三種方法(設定正確的FileShare權限)呢?

    ReplyDelete