由於有很多系統都在調用此web service,也就是說對此log.txt的訪問實際爲併發操作。這也就存在一個併發控制的問題:
{
Monitor.Enter(lockObject);
System.IO.StreamWriter sw = null;
try
{
string logFile = Server.MapPath("~/Log/" + this.GetType().Name + ".txt");
string fullText = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\t" + HttpContext.Current.Request.UserHostAddress + "\t" + detailDesc;
checkFile(logFile);
sw = System.IO.File.AppendText(logFile);
sw.WriteLine(fullText);
sw.Flush();
}
finally
{
Monitor.Exit(lockObject);
sw.Close();
}
}
在多線程的環境中爲了避免同一時間有多線程同時執行寫檔的程序而造成例外狀況。
理論上看似不會出問題的程序跑到catch的區段?錯誤信息如下:
The process cannot access the file 'D:\log_20100630.txt' because it is being used by another process.
到底出了什麼問題?另外msdn中查到
請使用 Enter,來取得當做參數傳遞之物件上的 Monitor。如果有其他執行緒已在物件上執行過 Enter,但還沒有執行對應的 Exit,目前的執行緒會阻斷,直到另一執行緒釋出物件為止。同一個執行緒叫用 Enter 不只一次而不發生封鎖是合法的情形;不過,必須先叫用等數量的 Exit 呼叫,等候物件的其他執行緒才會解除封鎖。
問題很簡單,sw.Close()放在了 Monitor.Exit(lockObject);之後。我們知道 Monitor.Exit(lockObject)用於unlock鎖定的對象,使得程序可被進入。而sw.Close()用於釋放sw資源,關閉txt文本,使得下一個process可以打開並訪問。而如果按照現在的執行邏輯就可能造成:unlock對象後,在釋放sw資源資源之前就有另一個process進入訪問,必然出錯。故而應先釋放資源,而後Unlock纔可保證併發操作正常進行。
而實際上我們知道,對於sw這類非託管資源,他們都實現了IDisposable 接口,我們可以用更簡單的方法來控制資源的釋放:那就是利用Using關鍵字。
{
Monitor.Enter(lockObject);
try
{
string logFile = HttpContext.Current.Server.MapPath("~/Log/" + this.GetType().Name + "_" + DateTime.Now.ToString("yyyyMMdd") + ".txt");
string fullText = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\t" + HttpContext.Current.Request.UserHostAddress + "\t" + detailDesc;
checkFile(logFile);
using (System.IO.StreamWriter sw = System.IO.File.AppendText(logFile))
{
sw.WriteLine(fullText);
// sw.Flush();
// sw.Close();
// sw.Dispose();
}
}
catch(Exception ex)
{
}
finally
{
Monitor.Exit(lockObject);
}
}
以下是測試代碼:
{
class Program
{
private static object lockObject = new object();
static void Main(string[] args)
{
Thread thread0 = Thread.CurrentThread;
thread0.Name = "thread Current";
Thread newThread = new Thread(Program.StartWork);
newThread.Name = "thread 1";
Thread newThread2 = new Thread(Program.StartWork);
newThread2.Name = "thread 2";
Thread newThread3 = new Thread(Program.StartWork);
newThread3.Name = "thread 3";
Thread newThread4 = new Thread(Program.StartWork);
newThread4.Name = "thread 4";
Thread newThread5 = new Thread(Program.StartWork);
newThread5.Name = "thread 5";
newThread.Start(1000);
newThread2.Start(1000);
newThread3.Start(1000);
newThread4.Start(1000);
newThread5.Start(1000);
StartWork(1000);
Console.WriteLine("Done");
Console.Read();
}
public static void StartWork(object n)
{
for (int i = 0; i < Convert.ToInt32(n); i++)
writeLog(i);
}
public static void writeLog(object i)
{
Monitor.Enter(lockObject);
try
{
string logFile = @"D:\log.txt";
string fullText = Thread.CurrentThread.Name.ToString() + "\t" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\t" + i;
//對於非託管資源,利用Using。Using語句確保調用 Dispose,即使在調用對象上的方法時發生異常也是如此.可使得資源自動釋放
using (System.IO.StreamWriter sw = System.IO.File.AppendText(logFile))
{
sw.WriteLine(fullText);
//sw.Flush();
//sw.Dispose();
}
}
catch (Exception ex)
{
Console.WriteLine("Exception:" + ex.Message);
}
finally
{
Monitor.Exit(lockObject);
}
#region 有問題的Code
//Monitor.Enter(lockObject);
//System.IO.StreamWriter sw = null;
//try
//{
// string logFile = @"D:\log.txt";
// string fullText = Thread.CurrentThread.Name.ToString() + "\t" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\t" + i;
// sw = System.IO.File.AppendText(logFile);
// sw.WriteLine(fullText);
// sw.Flush();
//}
//catch (Exception ex)
//{
// Console.WriteLine("Exception:" + ex.Message);
//}
//finally
//{
// Monitor.Exit(lockObject);
// sw.Close();
//}
#endregion
}
}
}