2010-07-23 19 views
17

Cada subproceso tiene su propia pila, pero comparten un montón común.¿Por qué los subprocesos comparten el espacio de montón?

Está claro para todos que la pila es para variables locales/de método & montón es, por ejemplo,/clase de variables.

¿Cuál es la ventaja de compartir el montón entre los hilos.

Hay varios hilos que se ejecutan simultáneamente, por lo que compartir la memoria puede provocar problemas como la modificación simultánea, la exclusión mutua, etc. Qué contenidos comparten los subprocesos en el montón.

¿Por qué es este el caso? ¿Por qué no hacer que cada hilo tenga su propio montón también? ¿Alguien puede proporcionar un ejemplo del mundo real de esto, cómo la memoria compartida es utilizada por los hilos?

+2

Esta pregunta realmente podría usar alguna aclaración. Por ejemplo, ¿qué se entiende por "idea práctica" y "ejemplo en tiempo real"? Además, suena como tarea ... Por favor marque como tal si lo es. – jdmichal

+1

Lo he reescrito, pero si esta no es la intención de la pregunta, gírela o modifíquela. –

+0

No, no está claro para todos que el montón es, por ejemplo,/variables de clase. Hay otras variables útiles para almacenar en el montón, y en muchos idiomas las variables de instancia/clase ocurren en la pila. – Puppy

Respuesta

34

¿Qué hace cuando quiere pasar datos de un hilo a otro? (Si nunca hizo que estaría escribiendo programas separados, no un programa multi-roscado.) Hay dos enfoques principales:

  • El enfoque que parece dar por sentado es memoria compartida: excepción para los datos que tienen un motivo convincente para ser específicos del subproceso (como la pila), todos los datos son accesibles para todos los subprocesos. Básicamente, hay un montón compartido. Eso le da velocidad: cada vez que un hilo cambia algunos datos, otros hilos pueden verlo. (Limitación: esto no es cierto si los subprocesos se ejecutan en diferentes procesadores: allí el programador necesita trabajar especialmente duro para usar la memoria compartida correctamente y de manera eficiente). La mayoría de los lenguajes imperativos principales, en particular Java y C#, favorecen este modelo.

    Es posible tener un montón por subproceso, más un montón compartido. Esto requiere que el programador decida qué datos colocar, y que a menudo no encaja bien con los lenguajes de programación existentes.

  • El doble enfoque es mensaje que pasa: cada subproceso tiene su propio espacio de datos; cuando un hilo quiere comunicarse con otro hilo necesita enviar un mensaje explícitamente al otro hilo, para copiar los datos del montón del remitente al montón del destinatario. En este contexto, muchas comunidades prefieren llamar a los procesos de subprocesos. Eso le da seguridad: dado que un hilo no puede sobreescribir la memoria de otro hilo por capricho, se evitan muchos errores. Otro beneficio es distribución: puede hacer que sus hilos se ejecuten en máquinas separadas sin tener que cambiar una sola línea en su programa. Puede encontrar bibliotecas de paso de mensajes para la mayoría de los idiomas, pero la integración tiende a ser menos buena. Los buenos lenguajes para entender el envío de mensajes son Erlang y JoCaml.

    De hecho, los entornos de paso de mensajes suelen utilizar memoria compartida detrás de la escena, al menos mientras los hilos se ejecutan en la misma máquina/procesador. Esto ahorra mucho tiempo y memoria ya que pasar un mensaje de un hilo a otro no requiere hacer una copia de los datos. Pero dado que la memoria compartida no está expuesta al programador, su complejidad inherente se limita a la implementación del lenguaje/biblioteca.

+1

Excelente respuesta. De hecho, algunos sistemas operativos más antiguos trataban todos los programas del sistema esencialmente como hilos en un gran proceso de sistema (¿creo que System/360 lo hizo?). La diferencia filosófica entre la memoria compartida y el envío de mensajes está en el corazón de las diferencias de diseño entre Windows y Unix, incluso hoy en día. –

+0

@Daniel: muchos sistemas integrados todavía funcionan, porque forzar la separación de procesos es costoso cuando cuenta su memoria en kB y requiere soporte de hardware (generalmente a través de una MMU). No entiendo en qué se diferencian Windows y Unix en su tratamiento de la concurrencia, ¿podría explicar un poco? – Gilles

+1

Lo que quiero decir es que la plataforma de Windows favorece las soluciones de memoria compartida, con soporte de nivel de sistema operativo para enhebrar. Por otro lado, Unix tradicionalmente ha preferido la comunicación a través de tuberías y enchufes sobre soluciones de memoria compartida. De ninguna manera es una distinción difícil y rápida, ya que ambas soluciones están disponibles en ambas plataformas, pero cada una tiene su forma "preferida", y eso lleva a la "diferencia filosófica" que describí en mi comentario. –

3

Los procesos no suelen compartir el espacio de almacenamiento dinámico. Hay API para permitir esto, pero el valor predeterminado es que los procesos están separados

Los subprocesos comparten espacio en montón.

Esa es la "idea práctica", dos formas de usar la memoria, compartida y no compartida.

+0

pueden compartir el espacio de almacenamiento dinámico; las API de memoria compartida lo proporcionan. Ah, y Windows 3.1 -> montones compartidos de Windows Me :) – gbjbaanb

+1

Requiere API especiales para lograr, no la predeterminada. –

+0

En Linux puede compartir lo que quiera con 'clone()'. –

2

En muchos idiomas/tiempos de ejecución, la pila se usa (entre otros) para mantener parámetros de función/método y variables. Si el hilo compartió una pila, las cosas se pondrían realmente desordenadas.

void MyFunc(int a) // Stored on the stack 
{ 
    int b; // Stored on the stack 
} 

Cuando se realiza la llamada a 'MyFunc', el apilados se extrae y A y B ya no está en la pila. Debido a que los hilos no comparten pilas, no hay problemas de enhebrado para las variables a y b.

Debido a la naturaleza de la pila (empujar/hacer estallar) no es realmente adecuado para mantener el estado 'a largo plazo' o el estado compartido a través de las llamadas a funciones. De esta manera:

int globalValue; // stored on the heap 

void Foo() 
{ 
    int b = globalValue; // Gets the current value of globalValue 

    globalValue = 10; 
} 

void Bar() // Stored on the stack 
{ 
    int b = globalValue; // Gets the current value of globalValue 

    globalValue = 20; 
} 


void main() 
{ 
    globalValue = 0; 
    Foo(); 
    // globalValue is now 10 
    Bar(); 
    // globalValue is now 20 
} 
0

Eso es porque la idea de los hilos es "compartir todo". Por supuesto, hay algunas cosas que no puedes compartir, como el contexto del procesador y la pila, pero todo lo demás se comparte.

1

The Heap es simplemente toda la memoria fuera de la pila que se asigna dinámicamente. Dado que el sistema operativo proporciona un único espacio de direcciones, queda claro que, por definición, el montón está compartido por todos los hilos del proceso. En cuanto a por qué las pilas no se comparten, eso se debe a que un hilo de ejecución debe tener su propia pila para poder gestionar su árbol de llamadas (¡contiene información sobre qué hacer cuando abandonas una función, por ejemplo!).

Ahora, por supuesto, podría escribir un administrador de memoria que asignara datos de diferentes áreas en su espacio de direcciones dependiendo del hilo de llamada, pero otros hilos podrían ver esos datos (como si de algún modo se filtrara un puntero a algo en la pila de su hilo a otro hilo, ese otro hilo podría leerlo, a pesar de ser una idea horrible)

+0

Para ser pedante, muchos administradores de memoria _de hecho_ asignan memoria desde diferentes áreas (arenas), pero lo hacen para mejorar el rendimiento. Por supuesto, la memoria resultante aún se comparte. – ninjalj

11

Porque de lo contrario serían procesos. Esa es la idea completa de hilos, compartir memoria.

1

El problema es que tener montones locales agrega complejidad significativa por muy poco valor.

Hay una pequeña ventaja en el rendimiento y esto se maneja bien con TLAB (Thread Local Allocation Buffer) que le ofrece la mayor parte de la ventaja de forma transparente.

1

En una aplicación multiproceso, cada subproceso tendrá su propia pila pero compartirá el mismo montón. Es por esto que se debe tener cuidado en su código para evitar cualquier problema de acceso concurrente en el espacio de almacenamiento dinámico. La pila es segura para los hilos (cada hilo tendrá su propia pila) pero el montón no es seguro para los hilos a menos que esté protegido con la sincronización a través de su código.

Cuestiones relacionadas