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

发布于 2024-12-11 00:02:17 字数 2765 浏览 0 评论 0原文

我正在使用 VS 2005,

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

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

由于该函数被多次调用,因此执行“new”并不是很有效,我正在寻找一种将所有值传递给另一个线程而无需动态内存分配的方法。

我想到的另一种选择是不“新建”数组列表,而是继续添加它,但问题是数组列表的大小可能会变得巨大。

有什么建议吗?

这是代码

//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)
      {

      }
    }
  }
}

I am using VS 2005

I have about 10-12 variables of different types such as double, int, string, long and bool that I want to log on another thread with min. overhead.

Currently, each time when I have to log, I "new" an arraylist, add all variables in it and then pass it as an argument to LoggerClass.Log(). In LoggerClass it sets/resets the event and another thread reads the value of arraylist.

Since the function gets called a lot, doing "new" is not very efficient and I am looking for a way to pass all the values to another thread without dynamic memory allocation.

One alternate in my mind is to not "new" the arraylist but just keep adding to it but the problem with that is the size of arraylist can become enoromous.

Any suggestions?

Heres the code

//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)
      {

      }
    }
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

网白 2024-12-18 00:02:17

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

收集未使用的列表,从空开始。当集合中有一个列表时,获取该列表(并将其从集合中删除)。否则,创建一个新的。完成日志记录后,Clear() 列表并将其返回到集合中。

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

If you're really sure that the problem is allocating new list every time, you can recycle them.

Have a collection of unused lists, that starts empty. When there is a list in the collection, take that (and remove it from the collection). Otherwise, create a new one. When you finish logging, Clear() the list and return it to the collection.

Of course, you access the collection from multiple threads, so you have make sure you're accessing it in a thread-safe manner.

浮光之海 2024-12-18 00:02:17

我没有做过任何测量,我不一定同意你的结论或你对这种类型的日志记录的方法,但我不会让这阻止我提出建议......

//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,因为它刚刚发布了一个重要的新版本。

I haven't done any measurements and I don't necessarily agree with your conclusions or your approach to this type of logging, but I'm not going to let that stop me from making a suggestion...

//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
  }
}

There is still a new in there, but this approach (creating a new logging record for each logging statement) is used by other popular logging frameworks like log4net, NLog, and EnterpriseLibrary. As far as I know, you don't hear many complaints about performance in those frameworks.

I don't know if this is any better or worse than what you have. Also, I don't know if it addresses the performance problem that you said that you have seen. On the other hand, it does simplify the code quite a bit, both for creating the logging message and for combining it into a string for writing to the stream.

If you are extremenly concerned with peformance and if your information is logged a thousand times per second, you might look at NLog's CurrentTimeGetter implementation. Its purpose is to optimize time retrieval, given that the time often does not change from one logged message to the next. You can find it at this link:

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

My ultimate recommendation would probably be to try to use an existing logging framework like log4net or NLog. These days I personally would recommend giving NLog a try first as it has just had a major new release.

沫离伤花 2024-12-18 00:02:17

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));     
} 

DateTime.Now.ToString() may be a problem. Perhaps consider using something else? This gave me quite a boost in performance --

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));     
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文