在线程之间传递大量变量以便有效地进行日志记录

时间:2011-10-19 17:03:21

标签: c# performance memory dynamic allocation

我正在使用VS 2005

我有大约10-12个不同类型的变量,例如double,int,string,long和bool,我想用min记录另一个线程。开销。

目前,每当我必须登录时,我“新”一个arraylist,在其中添加所有变量,然后将其作为参数传递给LoggerClass.Log()。在LoggerClass中,它设置/重置事件,另一个线程读取arraylist的值。

由于函数被调用很多,所以执行“new”效率不高,我正在寻找一种方法将所有值传递给另一个没有动态内存分配的线程。

在我的脑海中,一个替代方案是不要“新”这个arraylist但只是继续添加它,但问题是arraylist的大小可能变得非常。

有什么建议吗?

继承代码

//This function gets called a thousand times per second

private void MyLogic(MyClass LogObj)
{

  ArrayList MyArrList = new ArrayList(15);//This line causes periodic performance outliers
                            MyArrList.Add("Q");
                            MyArrList.Add(LogObj.valString);
                            MyArrList.Add(LogObj.ValDouble);
                            MyArrList.Add(LogObj.ValInt);
                            MyArrList.Add(LogObj.valString2);
                            MyArrList.Add(LogObj.ValDouble2);
                            MyArrList.Add(LogObj.ValInt2);
                            MyArrList.Add(LogObj.valString3);
                            MyArrList.Add(LogObj.ValDouble3);
                            MyArrList.Add(LogObj.ValInt3);
                            MyArrList.Add(LogObj.valString4);
                            MyArrList.Add(LogObj.ValDouble4);
                            MyArrList.Add(LogObj.ValInt4);

  MyLogger.Log(MyArrList);
}


Class MyLogger
{
  private Queue         m_logQueue;

  public void Log(ArrayList LogArr) 
  {
    lock (m_LogQueue)
    {
      m_LogQueue.Enqueue(LogArr);
    }

    m_Event.Set();
  }
}

private void LogThread()
{           
  ArrayList ValuesToLog = new ArrayList(); 
  StringBuilder StringBuf = new StringBuilder(); 
  string csFileName = "MyLog";

  using (StreamWriter sw = new StreamWriter(new FileStream(csFileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)))
  {
    while (m_bRun)
    {
      m_Event.WaitOne();
      try 
      {
        while (true)
        {
          lock (m_logQueue)
          {
            if (m_logQueue.Count > 0)
            {
              ValuesToLog = (ArrayList)m_logQueue.Dequeue();                                     
            }
            else
              break;
          }

          StringBuf = new StringBuilder();
          StringBuf.Append(DateTime.Now);
          StringBuf.Append(",");

          for (int i = 0; i < (ValuesToLog.Count - 1); i++)
          {
            StringBuf.Append(",");
            StringBuf.Append(ValuesToLog[i]);
          }

          sw.WriteLine(StringBuf.ToString());

          if (m_bAbort) break;
          if (m_EventStop.WaitOne(0, true))
            break;
        }
        sw.Flush();
      }
      catch(Exception e)
      {

      }
    }
  }
}

3 个答案:

答案 0 :(得分:0)

如果您确实每次都确定问题是分配新列表,则可以回收它们。

拥有一组未使用的列表,这些列表从空开始。如果集合中有列表,请将其删除(并从集合中删除)。否则,创建一个新的。完成日志记录后,Clear()列表并将其返回到集合。

当然,您从多个线程访问该集合,因此您必须确保以线程安全的方式访问它。

答案 1 :(得分:0)

我没有做任何测量,我不一定同意你的结论或你采用这种类型的测井方法,但我不会让那些阻止我提出建议......

//Use this to hold the information that you want to log.
public class LogRecord
{
  private IEnumerable<object> values;

  public LogRecord(params object [] data)
  {
    LogTime = DateTime.Now;
    values = data;
  }

  public DateTime LogTime { get; set; }
  public IEnumerable<object> Values 
  { 
    get
    {
      yield return LogTime;
      foreach(var v in values) yield return v;
    }
  }
}

//
private void MyLogic(MyClass LogObj)
{
  var record = new LogRecord("Q", LogObj.Var1, LogObj.Var2, LogObj.Var3);
  MyLogger.Log(record);
}

class MyLogger
{
  public void Log(LogRecord record)
  {
    //Do a bunch of threading stuff...

    string msg = string.Join(",", record.Values);

    //Write the message to the stream
  }
}

还有一个new,但这种方法(为每个日志记录语句创建一个新的日志记录)被其他流行的日志记录框架(如log4net,NLog和EnterpriseLibrary)使用。据我所知,在这些框架中你没有听到很多关于绩效的抱怨。

我不知道这比你的更好还是更坏。此外,我不知道它是否解决了您所说的性能问题。另一方面,它确实简化了代码,无论是创建日志消息还是将其组合成字符串以便写入流。

如果您非常关注性能,如果您的信息每秒记录数千次,您可以查看NLog的CurrentTimeGetter实现。其目的是优化时间检索,因为时间通常不会从一条记录消息变为下一条消息。您可以在以下链接找到它:

https://github.com/jkowalski/NLog/blob/master/src/NLog/Internal/CurrentTimeGetter.cs

我的最终建议可能是尝试使用现有的日志框架,如log4net或NLog。这些天我个人会建议首先尝试NLog,因为它刚刚发布了一个重要的新版本。

答案 2 :(得分:0)

DateTime.Now.ToString()可能是个问题。也许考虑使用别的东西?这给了我很大的提升 -

char[] buffer = new char[21];

static string DateTimeNowToString()     
{

    var now = DateTime.Now;
    buffer[2] = buffer[5] = '.';         
    buffer[8] = ' ';         
    buffer[11] = buffer[14] = buffer[17] = ':';         
    Write2(buffer, now.Month, 0);         
    Write2(buffer, now.Day, 3);         
    Write2(buffer, now.Year % 100, 6);         
    Write2(buffer, now.Hour, 9);         
    Write2(buffer, now.Minute, 12);         
    Write2(buffer, now.Second, 15);         
    Write3(buffer, now.Millisecond, 18);

    return new String(buffer)
}     

static void Write2(char[] buffer, int value, int offset)     
{         
    buffer[offset++] = (char)('0' + (value / 10));         
    buffer[offset] = (char)('0' + (value % 10));     
}

static void Write3(char[] buffer, int value, int offset)     
{        
    buffer[offset++] = (char)('0' + (value / 100));         
    buffer[offset++] = (char)('0' + ((value / 10) % 10));         
    buffer[offset] = (char)('0' + (value % 10));     
}