我在C#中創建一個日誌記錄類,我需要它是線程安全的。我實現了TextWriter.Synchronized和鎖,但我遇到了一個非常奇怪的問題,他們似乎無法正常工作。c#線程安全日誌記錄問題不使用singleton
我不想使用單例或靜態類,因爲我希望在任何給定的時間能夠擁有這個日誌記錄類的多個實例,並且我想根據日誌的文件名來同步這些線程。所以如果我有30個線程和3個不同的Log類的實例,它們都使用相同的日誌文件,它將正確同步並且沒有任何問題。以下是我到目前爲止所做的。我遺漏了一些不相關的代碼,比如構造函數和close/dispose。
public class Log : IDisposable
{
public enum LogType
{
Information,
Warning,
Error
}
private FileStream m_File;
private TextWriter m_Writer;
private string m_Filename;
//this is used to hold sync objects per registered log file
private static SortedList<string, object> s_SyncObjects = new SortedList<string, object>();
//this is used to lock and modify the above variable
private static readonly object s_SyncRoot = new object();
public void WriteLine(Log.LogType MsgType, string Text)
{
//this is the problem i think, the lock isn't functioning correctly
//see below this code for an example log file with issues
lock (Log.s_SyncObjects[this.m_Filename])
{
this.m_Writer.WriteLine(DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss:fffffff") + " " + MsgType.ToString() + ": " + Text);
}
return;
}
public void Open(string Filename)
{
//make filename lowercase to ensure it's always the same
this.m_Filename = Filename.ToLower();
this.m_File = new FileStream(Filename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
this.m_Writer = TextWriter.Synchronized(new StreamWriter(this.m_File) { AutoFlush = true });
//lock the syncroot and modify the collection of sync objects
//this should make it so that every instance of this class no matter
//what thread it's running in will have a unique sync object per log file
lock (Log.s_SyncRoot)
{
if (!Log.s_SyncObjects.ContainsKey(this.m_Filename))
Log.s_SyncObjects.Add(this.m_Filename, new object());
}
}
}
爲了測試這個我創建指向相同的日誌文件記錄器的3個實例,創建30個線程和分配每個線程記錄器(一個以1,2,3,1,2, 3),然後我運行所有30個線程,直到我按q。
這對於在日誌文件中逐行寫入以及按照正確的順序保持寫入時間的作用很大,但這裏是我在日誌文件中獲得的內容。看起來線程覆蓋了日誌文件的一部分,它似乎在不同線程上記錄器的不同實例發生,而不是不同線程上記錄器的同一實例。下面的日誌文件包含創建條目的時間,記錄器ID(基於1),線程ID(基於0)和消息「test」。
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3479116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3479116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3479116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3479116 Information: LOGID08/27/2012 11:47:34:3479116 Information: LOGID: 3, THREADID: 23, MSG: test
08/27/2012 11:47:34:3479116 08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
請注意,其中2行已被破壞。我猜這是由於鎖不能正常工作,或者我錯誤地使用鎖。我也不想使用排隊或任何類型的單身人士。如果我將WriteLine中的鎖更改爲m_SyncRoot變量並將其設置爲非靜態,此行爲似乎不會發生。我不知道爲什麼這樣做,但對我來說,這似乎不是我想要做的。我也不想單獨鎖定靜態m_SyncRoot,因爲如果我有3個記錄器實例指向3個不同的日誌文件,那麼每個文件都會無緣無故地阻塞另一個。
我對此很失落,我完全搞砸了嗎?
如果有人需要它,這裏是測試類用於生成的線程
public class LogTest
{
private Log m_Log1;
private Log m_Log2;
private Log m_Log3;
private Thread[] m_Threads;
private const int THREAD_COUNT = 30;
private bool m_Done;
public LogTest()
{
this.m_Log1 = new Log();
this.m_Log2 = new Log();
this.m_Log3 = new Log();
this.m_Log1.Open("test.txt");
this.m_Log2.Open("test.txt");
this.m_Log3.Open("test.txt");
this.m_Threads = new Thread[THREAD_COUNT];
this.m_Done = false;
}
public void run()
{
for (int i = 0; i < THREAD_COUNT; i++)
{
Thread th = new Thread(new ParameterizedThreadStart(this.LogThread));
this.m_Threads[i] = th;
}
for (int i = 0; i < THREAD_COUNT; i++)
{
int logId = 1;
Log temp = this.m_Log1;
if ((i % 3) == 1)
{
temp = this.m_Log2;
logId = 2;
}
else if ((i % 3) == 2)
{
temp = this.m_Log3;
logId = 3;
}
this.m_Threads[i].Start(new object[] { logId, i, temp });
}
ConsoleKeyInfo key = new ConsoleKeyInfo();
while ((key = Console.ReadKey()).KeyChar != 'q')
;
this.m_Done = true;
}
private void LogThread(object state)
{
int loggerId = (int)((object[])state)[0];
int threadId = (int)((object[])state)[1];
Log l = (Log)((object[])state)[2];
while (!this.m_Done)
{
l.WriteLine(Log.LogType.Information, String.Format("LOGID: {0}, THREADID: {1}, MSG: {2}", loggerId, threadId, "test"));
}
}
}
編輯:編輯以改變靜態M_到S_所建議,並添加了自動沖洗屬性到的StreamWriter;將其設置爲true ...仍然無效。
代碼複審:'m_'前綴不用於靜態變量。 's_'是可以接受的。 – Ankush
看看[VS2010中的concurrent_queue容器](http://blogs.msdn.com/b/nativeconcurrency/archive/2009/11/23/the-concurrent-queue-container-in-vs2010.aspx) – lsalamon
什麼是log4net或NLog? – GSerjo