0

以下是失败的集成测试的伪代码:

[测试]

void TestLogger()
    // Init static logger
    Logger.Init(pathToFile);

    // Create five threads that will call LogMessages delegate
    for i = 1 to 5 
    {
       Thread aThread = new Thread(LogMessages)
       aThread.Start(i);
    }

    // let threads complete their work
    Thread.Sleep(30000);

    /// read from log file and count the lines
    int lineCount = GetLineCount();

    // 5 threads, each logs 5 times = 25 lines in the log
    Assert.Equal(25, lineCount);


static void LogMessages( object data )
  // each thread will log five messages
  for i = 1 to 5 
  Logger.LogMessage(i.ToString() + " " + DateTime.Now.Ticks.ToString());
  Thread.Sleep(50);

每次运行测试时,行数似乎都会发生变化。有时行数是 23,有时是 25。

在深入研究代码后,我注意到日志文件同时被多个线程访问(通过滴答计数相同来验证)。对此文件的访问没有锁定,但同时我没有看到抛出异常。谁能解释为什么运行之间的日志行数不一致?此外,这是多个线程同时写入同一个文件的负面影响吗?

4

4 回答 4

5

众所周知,竞争条件是不可预测的。您永远不知道有多少写入无法正常工作 - 有时它甚至可能完全正常工作。是的,这是在不同步的情况下从多个线程写入同一个文件的负面影响。

于 2012-07-05T17:36:47.487 回答
1

如果您尝试使用 Environment.TickCount 验证同时性,那么您将度过一段糟糕的时光。它只有大约 15 毫秒 (IIRC) 的精度,所以如果两个线程的值相同,那么你真正知道的就是它们的日志记录彼此相差约 15 毫秒。

If your Logger class puts a lock around its access to the log file, that should be sufficient. Just create a sync object via private static readonly object sync = new object(); and then do lock (sync) { ... open/read/write the file ... }. Otherwise you'll be at the mercy of the thread safety of whatever type of Stream you're using (hint: in general they're not thread safe).

于 2012-07-05T18:20:31.443 回答
1

Perhaps the simplest way to handle these sorts of producer/consumer deadlocks and other race conditions is to invoke the lock() builtin:

lock(Logger){
 //use your logger here
}

This will hold the other threads at bay. You can also use the sync lock style as mentioned briefly above. There's a good example of all the options available (and pros and cons) on this guy's site:

http://www.gavindraper.co.uk/2012/02/05/thread-synchronizationlocking-in-net/

Good luck.

于 2012-07-05T18:45:08.987 回答
1

Typically, multiThreaded logging is performed by queueing log entries, (blocking producer-consumer queue), to one logger thread that writes to the disk. This keeps the lock time down to the time taken to push a *log entry onto the queue: next-to-no contention. The queue absorbs any disk latency, network delays etc.

Using a simple lock inflicts any disk/network latency/delay upon all calling threads.

于 2012-07-05T20:25:47.380 回答