Подтвердить что ты не робот

Почему потоки разделяют кучу пространства?

Каждый поток имеет свой собственный стек, но у них есть общая куча.

Он ясно для каждого, что стек для локальных/методов переменных, а куча - это переменные типа/класса.

В чем преимущество совместного использования кучи между потоками.

Существует несколько потоков, выполняемых одновременно, поэтому совместная память может привести к таким проблемам, как одновременная модификация, взаимное исключение и т.д. Какое содержимое разделяется потоками в куче.

Почему это так? Почему у каждой нити тоже есть своя куча? Может ли кто-нибудь представить реальный пример этого, как разделяемая память используется потоками?

4b9b3361

Ответ 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

Это потому, что идея потоков - это "поделиться всем". Конечно, есть некоторые вещи, которые вы не можете использовать, например, контекст процессора и стек, но все остальное является общим.