java - utilizar - ¿Cuáles son las ventajas del almacenamiento local de subprocesos a nivel de instancia?
ventajas de usar lenguaje de programacion java (6)
¿Qué problemas resuelve el almacenamiento local de subprocesos o qué ventajas proporciona el almacenamiento local de subprocesos sobre el lenguaje estándar orientado a objetos de crear instancias de objetos independientes para contener datos locales de subprocesos?
El almacenamiento local de subprocesos le permite proporcionar a cada subproceso en ejecución una instancia única de una clase, lo cual es muy valioso cuando se intenta trabajar con clases que no son seguras para subprocesos o cuando se intenta evitar los requisitos de sincronización que pueden ocurrir debido al estado compartido.
En cuanto a la ventaja en comparación con su ejemplo: si está generando un solo hilo, hay poca o ninguna ventaja de usar el almacenamiento local de subprocesos sobre el paso en una instancia. ThreadLocal<T>
y construcciones similares se vuelven increíblemente valiosas, sin embargo, cuando se trabaja (directa o indirectamente) con un ThreadPool.
Por ejemplo, tengo un proceso específico en el que trabajé recientemente, en el que estamos realizando un cálculo muy pesado utilizando la nueva biblioteca paralela de tareas en .NET. Ciertas partes de los cálculos realizados pueden almacenarse en caché, y si la memoria caché contiene una coincidencia específica, podemos reducir un poco el tiempo al procesar un elemento. Sin embargo, la información almacenada en caché tenía un alto requisito de memoria, por lo que no queríamos almacenar más caché que el último paso de procesamiento.
Sin embargo, tratar de compartir este caché a través de hilos es problemático. Para hacerlo, tendríamos que sincronizar el acceso a él, y también agregar algunos controles adicionales dentro de nuestra clase para que sean seguros para los hilos.
En lugar de hacer esto, reescribí el algoritmo para permitir que cada hilo mantenga su propio caché privado en un ThreadLocal<T>
. Esto permite que los hilos mantengan su propio caché privado. Dado que el esquema de partición que utiliza el TPL tiende a mantener los bloques de elementos juntos, la memoria caché local de cada hilo solía contener los valores apropiados que necesitaba.
Esto eliminó los problemas de sincronización, pero también nos permitió mantener nuestro almacenamiento en caché en su lugar. El beneficio general fue bastante grande, en esta situación.
Para un ejemplo más concreto, eche un vistazo a esta publicación de blog que escribí sobre la agregación utilizando el TPL . Internamente, la clase Parallel usa un ThreadLocal<TLocal>
siempre que use la sobrecarga ForEach que mantiene el estado local (y también los métodos Parallel.For<TLocal>
). Así es como el estado local se mantiene separado por hilo para evitar el bloqueo.
Esta pregunta me llevó a preguntarme sobre el almacenamiento local de subprocesos en marcos de desarrollo de alto nivel como Java y .NET.
Java tiene una ThreadLocal<T>
(y quizás otras construcciones), mientras que .NET tiene ranuras de datos , y pronto una ThreadLocal<T>
propia. (También tiene el ThreadStaticAttribute
, pero estoy particularmente interesado en el almacenamiento local de subprocesos para los datos de los miembros). La mayoría de los otros entornos de desarrollo modernos proporcionan uno o más mecanismos, ya sea a nivel de lenguaje o marco.
¿Qué problemas resuelve el almacenamiento local de subprocesos o qué ventajas proporciona el almacenamiento local de subprocesos sobre el lenguaje estándar orientado a objetos de crear instancias de objetos independientes para contener datos locales de subprocesos? En otras palabras, ¿cómo es esto?
// Thread local storage approach - start 200 threads using the same object
// Each thread creates a copy of any thread-local data
ThreadLocalInstance instance = new ThreadLocalInstance();
for(int i=0; i < 200; i++) {
ThreadStart threadStart = new ThreadStart(instance.DoSomething);
new Thread(threadStart).Start();
}
Superior a esto?
// Normal oo approach, create 200 objects, start a new thread on each
for(int i=0; i < 200; i++) {
StandardInstance standardInstance = new StandardInstance();
ThreadStart threadStart = new ThreadStart(standardInstance.DoSomething);
new Thread(threadStart).Start();
}
Puedo ver que usar un solo objeto con almacenamiento de subprocesos puede ser un poco más eficiente en memoria y requerir menos recursos de procesador debido a menos asignaciones (y construcciones). ¿Hay otras ventajas?
Aquí hay un uso práctico de ThreadLocal: http://blogs.captechconsulting.com/blog/balaji-muthuvarathan/persistence-pattern-using-threadlocal-and-ejb-interceptors
Ayuda a pasar un valor por la pila. Es útil cuando necesita un valor en la pila de llamadas, pero no hay manera (o beneficio) de pasar este valor al lugar que se necesita como parámetro a un método. El ejemplo anterior de almacenar el HttpRequest actual en un ThreaLocal es un buen ejemplo de esto: la alternativa sería pasar el HttpRequest como parámetro de la pila al lugar donde se necesitaría.
Desea realizar una serie de llamadas, accediendo a alguna variable de forma ubicua. Puedes pasarlo como argumento en cada llamada.
function startComputingA(other args) {
global_v = create // declared locally
call A2(other args, global_v)
call A3(other args, global_v)
function A2(other args, global_v) {
call A3(other args, global_v)
function A3(other args, global_v) {
call A4(other args, global_v)
Todas sus funciones deben declarar global_v
argumento. Esto apesta Tiene un alcance global para almacenar variables globales y enrutarlo "virtualmente" a cada rutina
variable global_v;
function A() { // use global_v and call B() }
function B() { // use global_v and call C() }
Sin embargo, puede suceder que otro hilo comience a ejecutar algunas de estas funciones mientras tanto. Esto corromperá su variable global. Por lo tanto, desea que la variable sea visible globalmente para todas las rutinas, sin embargo, no entre subprocesos. Quieres que cada hilo tenga una copia separada de global_v
. Aquí es cuando el almacenamiento local es indispensable! global_v
como una variable de subproceso local. Por lo tanto, cualquier subproceso puede acceder a global_v
desde cualquier lugar, pero diferentes copias de él.
En Java, el almacenamiento local de Thread puede ser útil en una aplicación web en la que una Thread dada suele procesar una única solicitud. Tome Spring Security, por ejemplo, el filtro de seguridad realizará la autenticación y luego almacenará las credenciales de los usuarios en una variable local de Thread.
Esto permite que el código de procesamiento de la solicitud real tenga acceso a la información de solicitud / autenticación de los usuarios actuales sin tener que inyectar nada más en el código.
Solo de vez en cuando, es útil tener un estado de subproceso local. Un ejemplo es para un contexto de registro: puede ser útil establecer el contexto de la solicitud que está atendiendo actualmente, o algo similar, para que pueda clasificar todos los registros relacionados con esa solicitud.
Otro buen ejemplo es System.Random
en .NET. Es bastante común saber que no debe crear una nueva instancia cada vez que quiera usar Random
, por lo que algunas personas crean una sola instancia y la colocan en una variable estática ... pero eso es incómodo porque Random
no es seguro para subprocesos. En su lugar, realmente desea una instancia por subproceso, sembrada adecuadamente. ThreadLocal<T>
funciona muy bien para esto.
Ejemplos similares son la cultura asociada con un hilo o el contexto de seguridad.
En general, se trata de no querer pasar demasiado contexto por todas partes. Podría hacer que cada llamada a un solo método incluya un "RandomContext" o un "LogContext", pero se interponga en la limpieza de su API, y la cadena se rompería si alguna vez tuviera que llamar a otra API a la que volvería a llamar El tuyo a través de un método virtual o algo similar.
En mi opinión, los datos de subprocesos locales son algo que debe evitarse siempre que sea posible, pero ocasionalmente puede ser realmente útil.
Diría que, en la mayoría de los casos, puede evitar que sea estático, pero ocasionalmente es posible que desee información por instancia, por hilo. Nuevamente, vale la pena usar su juicio para ver dónde es útil.