如何正确锁定StringBuilder,以便它不会抛出ArgumentOutOfRangeException

时间:2017-12-15 14:17:50

标签: c# multithreading stringbuilder

我已经在堆栈溢出和其他地方读过多个帖子,StringBuilder不是Thread安全的,当从多个线程访问(读/写)时应该被锁定:herehere,{ {3}}。我不清楚如何锁定我的Stringbuilder实例 - 特别是应该添加多少锁以及在哪里。

我有以下代码(代码段):

    class MemoryTracker
    {
        private Process extProc    { get; set; }
        private StringBuilder sb   { get; set; }

        internal int trackByIP(string ip)
        {
             ...
             ...
             sb = new StringBuilder();
             extProc.OutputDataReceived  += new DataReceivedEventHandler((s, e)  => sb.Append(e.Data + "\n"));
             extProc.ErrorDataReceived   += new DataReceivedEventHandler((s, e)  => sb.Append(e.Data + "\n"));

             extProc.Start();
             extProc.PriorityClass       = ProcessPriorityClass.RealTime;
             ...
             ...
        }

        string getDataWStartEndPttrn(StringBuilder data, string strPttr, string endPttr, string extendedTerminator)
        {
             string s = data.ToString(); // <-- THROWS ArgumentOutOfRangeException 

             int si = getStartIdx(s, strPttr, patternDiff(strPttr, endPttr));
             int se = getEndIdx(s, endPttr, patternDiff(endPttr, strPttr));

             if (se >= 0 && si >= 0)
             {
                 string s1 = s.Substring(si, se - si);

                 string sTMP = s.Substring(se);
                 string s2 = s.Substring(se, sTMP.IndexOf(extendedTerminator));

                 return s1 + s2;
             }

             return "";
        }

放置锁后,我仍然看到同样的错误。

class MemoryTracker
{
    private Process extProc    { get; set; }
    private StringBuilder sb   { get; set; }
    private Object thisLock = new Object();


    string getDataWStartEndPttrn(StringBuilder data, string strPttr, string endPttr, string extendedTerminator)
    {
        lock (thisLock)
        {
             string s = data.ToString() ; // <-- STILL THROWS ArgumentOutOfRangeException 
        }
 ...
 ...

问题:如何正确考虑锁定/锁定的位置?我没有显式创建线程的地方,所以我认为它是StringBuilder.toString();

的线程安全用法

1 个答案:

答案 0 :(得分:3)

替代解决方案

围绕StringBuilder创建线程安全包装,使用它代替当前。这将封装包装类中的同步逻辑。我觉得这样做要好一些,而不是锁定使用StringBuilder类的方法。

使用ReaderWriteLockSlim。当获取读锁定时,它允许多个线程访问代码块,这是获取写锁定时唯一要访问的单个线程。简单地说,如果多个线程尝试使用.ToString()方法,那就没问题了,如果其他一些线程尝试使用.Append(),那么这个线程会等待所有其他线程读取(.ToString())完成。

这是非常基本的,它应该是一个很好的起点。

public class ThreadSafeStringBuilder 
{
   private readonly StringBuilder core = .....
   private readonly ReaderWriterLockSlim sync = ....

   public void Append(String str) {
      try {
         this.sync.EnterWriterLock();
         this.core.Append(str);
      }
      finally {
         this.sync.ExitWriterLock()
      }
    }

  public override ToString() {
    try {
       this.sync.EnterReadLock();
       return this.core.ToString();
    }
    finnaly {
       this.sync.ExitReadLock()
    }
  }
}

现在您可以使用线程安全包装器,而无需在任何地方同步对它的访问。

编辑2017-12-18 :来自评论的建议。你应该执行一些性能。测试。对于一个简单的场景lock statement更合适(检查你最糟糕的情况,比如你希望读或写多少线程等等,还要检查最好的情况......等等)。关于代码,只需用try语句替换try,finnaly语句,.Append().ToString()的锁应该锁定在同一个对象上。