practices net example best .net thread-safety stringbuilder

example - Es el hilo StringBuilder de.NET seguro



stringbuilder net (3)

La sección "Seguridad de subprocesos" de la documentación de MSDN para StringBuilder indica que:

... no se garantiza que los miembros de instancia sean seguros para subprocesos

pero esta declaración parece que se ha copiado y pegado para casi todas las clases en el Marco:

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

Sin embargo, estas publicaciones del blog de Gavin Pugh mencionan comportamientos seguros de subprocesos de 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/

Además, la fuente de StringBuilder revelada por Reflector, y los comentarios que la acompañan en la fuente de SSCLI, también sugieren muchas consideraciones de implementación para garantizar la seguridad de subprocesos:

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

¿Alguien tiene más información sobre si es seguro compartir una instancia de StringBuilder entre varios subprocesos simultáneos?


Absolutamente no; Aquí hay un ejemplo simple levantado de 4.0 a través del reflector:

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

El atributo solo maneja a las personas que llaman, no a la seguridad de subprocesos; Esto no es seguro para subprocesos.

Actualización: mirando la fuente a la que hace referencia, esto claramente no es la base de código de .NET 4.0 actual (comparando algunos métodos). Tal vez él está hablando de una versión particular de .NET, o quizás de XNA, pero no es el caso en general. El StringBuilder 4.0 no tiene un campo m_currentThread , que utiliza el material de origen de Gavin; hay una pista (un ThreadIDField constante no ThreadIDField ) que solía existir, pero ... ya no.

Si desea una refutación directa , ejecute esto en 4.0; lo más probable es que dé la longitud incorrecta (he visto unos pocos en la región 4k, unos pocos en la región 2k - debería ser exactamente 5000), pero algunos otros métodos de Append(char) por ejemplo) tienden a ser más probables Para lanzar excepciones, dependiendo del tiempo:

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


De la documentación de MSDN :

Cualquier miembro público estático (Compartido en Visual Basic) de este tipo es seguro para subprocesos. No se garantiza que ningún miembro de instancia sea seguro para subprocesos.


Todo el objetivo de la documentación es darle garantías. En este caso, en el caso de los miembros, no se garantiza que nada sea seguro para subprocesos y debe tratarlo como tal, por lo tanto, confiar en métodos de sincronización externos.

Que algunas cosas puedan ser seguras para las hebras es un detalle de implementación que puede y tal vez cambia de una versión del marco a la siguiente o de una implementación a la siguiente (de hecho, hay muchos de estos detalles que cambian en las versiones del marco; Eric Lippert tiene algunas puestos detallando algunos de ellos). No confíes en ello.

(En otras palabras: no escriba código en una implementación, escríbalo contra la interfaz y el contrato, que son los metadatos de la clase y su documentación en este caso).