使用FileShare.ReadWrite和useAsync =假
訪問通過創建使用this constructor(不要使用File.Open
)一個FileStream文件並指定以下參數:
var stream = new FileStream(path,
FileMode.Append,
FileAccess.Write,
FileShare.ReadWrite,
4096,
false);
的重要參數要注意的是FileShare.ReadWrite
和useAsync = false
。
FileShare.ReadWrite:允許隨後打開文件進行讀取或寫入。如果未指定此標誌,則在文件關閉之前,打開文件進行讀取或寫入(通過此進程或其他進程)的任何請求都將失敗。但是,即使指定了此標誌,訪問該文件仍可能需要其他權限。
useAsync:指定是否使用異步I/O或同步I/O。但是請注意,底層操作系統可能不支持異步I/O,因此在指定true時,可能會根據平臺同步打開句柄。當以異步方式打開時,BeginRead和BeginWrite方法在大型讀取或寫入時執行得更好,但對於小型讀取或寫入,它們可能會慢很多。如果應用程序旨在利用異步I/O,請將useAsync參數設置爲true。使用異步I/O正確地可以加快多達應用程序作爲10倍,但使用它而不用重新設計爲異步I應用程序/ O可以作爲10.
通過一個因子多達降低性能使用這些參數,您將獲得一個允許其他進程並行訪問文件的文件句柄。同時你的寫操作將是同步的,這將防止你的輸出被另一個進程分裂成一半。仍然存在鎖定,但它由底層操作系統處理,並且對您和任何競爭過程都是透明的。
添加鎖
如果它讓你感覺更好,你可以在鎖把它包:
lock (lockObject)
{
using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 4096, false))
{
var writer = new TextWriter(stream);
writer.Write(message);
}
}
注意,lock
保護您免受競爭的線程,而不是相互競爭的過程。如果您有一個處理日誌寫入的工作線程(例如,與隊列和生產者/消費者模式),你可能不需要它,它會增加不必要的開銷。但是,如果您直接從Web工作線程寫入日誌,則確實需要它。
跨進程互斥
以上應該是很要命的安全,即使是在一個應用程序池回收。至少我從來沒有遇到任何問題。但是..如果你是真的偏執和你的日誌是關鍵的任務,你可以使用named mutex鎖定跨越進程邊界。
var mutex = new Mutex(false, "MyLoggingMutex");
try
{
mutex.WaitOne();
using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 4096, false))
{
var writer = new TextWriter(stream);
writer.Write(message);
}
}
finally
{
mutex.ReleaseMutex();
}
有相當多的開銷有這樣的事情,所以我除非記錄很可能不會使用它的關鍵任務,如您將可審計的數據轉儲到可能用於支持不可否認性的日誌文件(例如,以證明誰做了什麼以免被起訴等等)。但如果真的是這樣的話,我會堅持使用數據庫,這使得問題變得微不足道。
你爲什麼要重新發明輪子並做自己的日誌?爲什麼不使用專爲此類事物設計的現有日誌記錄庫?像[Serilog](https://serilog.net/)或[Nlog](http://nlog-project.org/)。 – mason
你的觀點很好。對於IIS回收行爲的最小操作,網站上靜態鎖定的一般安全性似乎仍值得理解。 –
'有些人說永遠不要使用靜態鎖'對於伐木,這些人是正確的。如果你這樣做,你不能運行一個網絡花園。 https://abhijitjana.net/2010/10/01/what-is-the-difference-between-web-farm-and-web-garden/ – mjwills