Является ли .NET StringBuilder поточно-ориентированным - PullRequest
30 голосов
/ 12 января 2012

В обычном разделе «Thread Thread» документации MSDN для StringBuilder говорится, что:

... ни один из членов экземпляра не гарантированно безопасен для потока ...

но это утверждение похоже на то, что оно было скопировано и вставлено практически для каждого класса в Framework:

http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx

Однако в этих сообщениях Гевина Пью упоминается темабезопасное поведение StringBuilder:

http://www.gavpugh.com/2010/03/23/xnac-stringbuilder-to-string-with-no-garbage/

http://www.gavpugh.com/2010/04/01/xnac-avoiding-garbage-when-working-with-stringbuilder/

Кроме того, источник StringBuilder, раскрытый Reflector, и сопровождающие комментарии в источнике SSCLI,также предложите множество соображений по реализации для обеспечения безопасности потоков:

http://labs.developerfusion.co.uk/SourceViewer/browse.aspx?assembly=SSCLI&namespace=System.Text&type=StringBuilder

Кто-нибудь еще может понять, безопасен ли экземпляр StringBuilder для совместного использования несколькими одновременными потоками?

Ответы [ 3 ]

25 голосов
/ 12 января 2012

Абсолютно нет; Вот простой пример, снятый с 4.0 через отражатель:

[SecuritySafeCritical]
public StringBuilder Append(char value)
{
    if (this.m_ChunkLength < this.m_ChunkChars.Length)
    {
        this.m_ChunkChars[this.m_ChunkLength++] = value;
    }
    else
    {
        this.Append(value, 1);
    }
    return this;
}

Атрибут только обрабатывает абонентов, а не потокобезопасность; это абсолютно не потокобезопасно.

Обновление: глядя на источник, на который он ссылается, это явно не текущая кодовая база .NET 4.0 (сравнение нескольких методов). Возможно, он говорит о конкретной версии .NET, или, может быть, XNA - но это не в целом. В 4.0 StringBuilder нет поля a m_currentThread, которое использует исходный материал Гэвина; есть подсказка (неиспользованная константа ThreadIDField), что она использовала для существования, но ... больше нет.


Если вы хотите direct disproof - запустите это на 4.0; это, скорее всего, даст неправильную длину (я видел несколько в области 4k, несколько в области 2k - это должно быть ровно 5000), но некоторые другие методы Append (например, Append(char)) имеют тенденцию больше Вероятность возникновения исключений в зависимости от времени:

var gate = new ManualResetEvent(false);
var allDone = new AutoResetEvent(false);
int counter = 0;
var sb = new StringBuilder();
ThreadStart work = delegate
{
    // open gate when all 5 threads are running
    if (Interlocked.Increment(ref counter) == 5) gate.Set();
    else gate.WaitOne();

    for (int i = 0; i < 1000; i++) sb.Append("a");

    if (Interlocked.Decrement(ref counter) == 0) allDone.Set();
};
for(int i = 0 ; i < 5 ; i++)
{
    new Thread(work).Start();
}
allDone.WaitOne();
Console.WriteLine(sb.Length);
6 голосов
/ 12 января 2012

Весь смысл документации в том, чтобы дать вам гарантии. В этом случае для членов экземпляра не гарантируется поточно-ориентированная защита, и вы должны обращаться с ней как таковой, поэтому полагаясь на внешние методы синхронизации.

То, что некоторые вещи могут быть threadsafe - это подробность реализации , которая может и может измениться от одной версии платформы к следующей или от одной реализации к следующей (на самом деле Есть много таких деталей, изменяющихся в версиях фреймворка; у Эрика Липперта есть несколько постов, подробно описывающих некоторые из них). Не надейся на это.

(Другими словами: не пишите код для реализации, запишите его в соответствии с интерфейсом и контрактом, которые в данном случае являются метаданными класса и его документации.)

1 голос
/ 15 апреля 2014

Из документации MSDN :

Любые открытые статические (Shared в Visual Basic) члены этого типа Поток безопасно. Любые члены экземпляра не гарантированно являются потоками сейф.

...