Почему потоки разделяют кучу пространства?
Каждый поток имеет свой собственный стек, но у них есть общая куча.
Он ясно для каждого, что стек для локальных/методов переменных, а куча - это переменные типа/класса.
В чем преимущество совместного использования кучи между потоками.
Существует несколько потоков, выполняемых одновременно, поэтому совместная память может привести к таким проблемам, как одновременная модификация, взаимное исключение и т.д.
Какое содержимое разделяется потоками в куче.
Почему это так? Почему у каждой нити тоже есть своя куча? Может ли кто-нибудь представить реальный пример этого, как разделяемая память используется потоками?
Ответы
Ответ 1
Что вы делаете, когда хотите передавать данные из одного потока в другой? (Если вы никогда не делали этого, вы бы писали отдельные программы, а не одну многопоточную программу.) Существует два основных подхода:
-
Подход, который вы считаете само собой разумеющимся, разделяемая память: за исключением данных, у которых есть веская причина быть зависимыми от потока (например, стек), все данные доступны для все темы. В принципе, есть общая куча. Это дает вам скорость: в любой момент, когда поток изменяет некоторые данные, другие потоки могут видеть это. (Ограничение: это неверно, если потоки выполняются на разных процессорах: там программисту нужно работать особенно сложно для правильной и эффективной работы с общей памятью.) Большинство основных императивных языков, в частности Java и С#, предпочитают эту модель.
Возможно наличие одной кучи на поток, плюс общая куча. Для этого требуется, чтобы программист определил, какие данные следует размещать там, и что часто не хорошо связывается с существующими языками программирования.
-
Двойной подход - это передача сообщений: каждый поток имеет свое собственное пространство данных; когда поток хочет связываться с другим потоком, ему нужно явно отправить сообщение другому потоку, чтобы скопировать данные из кучи отправителя в кучу получателя. В этом режиме многие сообщества предпочитают вызывать процессы потоков. Это дает вам безопасность: поскольку нить не может перезаписать какую-либо другую память потоков по прихоти, многие ошибки устраняются. Другим преимуществом является распространение: вы можете создавать потоки на отдельных машинах без необходимости менять одну строку в своей программе. Вы можете найти библиотеки для передачи сообщений для большинства языков, но интеграция имеет тенденцию быть менее хорошими. Хорошие языки для понимания передачи сообщений: Erlang и JoCaml.
На самом деле среда передачи сообщений обычно использует разделяемую память за сценой, по крайней мере, пока потоки работают на одном компьютере/процессоре. Это экономит много времени и памяти с момента передачи сообщения из одного потока в другой, но не требует создания копии данных. Но поскольку разделяемая память не предоставляется программисту, ее присущая сложность ограничивается реализацией языка/библиотеки.
Ответ 2
Потому что иначе они будут процессами. Вот и вся идея потоков, чтобы поделиться памятью.
Ответ 3
Процессы не генерируют - кучу пространства. Для этого есть API, но по умолчанию это разные процессы
Пустое пространство кучи потоков.
Что "практическая идея" - два способа использования памяти - общие и не общие.
Ответ 4
Во многих языках/времени выполнения стек (среди прочих) используется для хранения параметров/переменных функции/метода. Если поток разделяет стек, все становится очень грязным.
void MyFunc(int a) // Stored on the stack
{
int b; // Stored on the stack
}
Когда вызов "MyFunc" завершен, стек выложен, а a и b больше не находятся в стеке. Поскольку потоки не разделяют стеки, для переменных a и b нет проблемы с потоками.
Из-за характера стека (push/popping) он не очень подходит для сохранения состояния "долговременного" состояния или общего состояния при вызове функций. Вот так:
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
}
Ответ 5
Куча - это всего лишь память за пределами стека, динамически распределенная. Поскольку ОС предоставляет одно адресное пространство, становится ясно, что куча по определению является общим для всех потоков в процессе. Что касается того, почему стеки не разделены, то из-за того, что поток выполнения должен иметь свой собственный стек, чтобы иметь возможность управлять своим деревом вызовов (он содержит информацию о том, что делать, когда вы оставляете функцию, например!).
Теперь вы могли бы, конечно, написать диспетчер памяти, который распределял данные из разных областей вашего адресного пространства в зависимости от вызывающего потока, но другие потоки все равно могли бы видеть эти данные (точно так же, как если бы вы каким-то образом пропустили указатель на что-то на вашем потоке стека на другой поток, этот другой поток мог прочитать его, несмотря на то, что это была ужасная идея)
Ответ 6
Проблема заключается в том, что наличие локальных куч добавляет значительную сложность при очень небольшом значении.
Существует небольшое преимущество в производительности, и это хорошо работает с помощью TLAB (Thread Local Allocation Buffer), который дает вам большую часть преимуществ прозрачно.
Ответ 7
В многопоточном приложении каждый поток будет иметь свой собственный стек, но будет разделять одну и ту же кучу. Вот почему вы должны соблюдать осторожность в своем коде, чтобы избежать проблем с одновременным доступом в куче. Стек является потокобезопасным (каждый поток будет иметь свой собственный стек), но куча не является потокобезопасной, если не защищена с помощью синхронизации через ваш код.
Ответ 8
Это потому, что идея потоков - это "поделиться всем". Конечно, есть некоторые вещи, которые вы не можете использовать, например, контекст процессора и стек, но все остальное является общим.