我已经在堆栈溢出和其他地方读过多个帖子,StringBuilder不是Thread安全的,当从多个线程访问(读/写)时应该被锁定:here,here,{ {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();
答案 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()
的锁应该锁定在同一个对象上。