Остается ли память выделенной при выходе из потока C ++? - PullRequest
5 голосов
/ 07 апреля 2009

Я использую библиотеку pthread в Linux.

Я присваиваю строку в потоке A, а затем пытаюсь напечатать строку в потоке B. Однако строка просто печатается пустой (я убедился, что она работает в потоке A).

Примечание. Строка находится внутри объекта, который, как я подозреваю, может быть очищен или восстановлен пустым ... Объект контейнера не дает мне ошибки сегмента или чего-то еще, просто все значения пусты.

Это связано с тем, что потоки не могут получить доступ к памяти из других потоков, или из-за того, что память не выделяется после остановки потока A? Или это не так; это может быть ошибкой в ​​моем коде, но я просто хотел исключить это ...

Обновление:

Оказывается, это была проблема с памятью. Благодаря вашим ответам, я также ответил на это сам , пожалуйста, прокомментируйте мой ответ, если вы согласны / не согласны.

Ответы [ 8 ]

9 голосов
/ 07 апреля 2009

Потоки, в отличие от процессов, совместно используют общее пространство памяти внутри процесса (каждый поток имеет свой собственный стек, но кучи обычно используются совместно). Поэтому при выходе из потока память, выделенная из общей кучи, не освобождается автоматически. Но, например, если вы поместили строковый объект в стек и передали его где-то с помощью простого указателя, деструктор освободит память при выходе из потока.

2 голосов
/ 07 апреля 2009

Хотя каждый поток имеет свой «собственный» фрагмент памяти (т. Е. Свое собственное пространство кучи) ... память - это память. Запуск потока ничего не делает с памятью другого потока. Как поток B получает указатель на строку в потоке A?

Вам нужно дать больше подробностей ... Вы кладете строку в кучу? Если его нет в куче, он, вероятно, исчезнет, ​​когда тема A умрет ... в противном случае у вас будет ошибка ... Пожалуйста, напишите больше!

1 голос
/ 08 апреля 2009

Хотя ваш «правильный путь» работает, это слишком много для вашей первоначальной проблемы.

Ниже приведено единственное изменение, которое было необходимо для вашей первоначальной проблемы.

void onlyFunctionRunFromThread2()
{
    MyType1 &mt1 = myMap[0];

   ...

По вашему «правильному пути», именно вы теперь несете ответственность за освобождение памяти, которую вы наделили новой. В вашем исходном примере это было сделано «автоматически» (так сказать).

И в обоих решениях отсутствует блокировка карты или элементов вашей карты, что является совершенно другим вопросом.

1 голос
/ 07 апреля 2009

Да, память остается выделенной.

Поставьте точку останова или войдите в dtor класса, содержащего строку, и посмотрите, что происходит.

Надеюсь, это поможет

0 голосов
/ 08 апреля 2009

Оказывается, проблема была вызвана неправильным использованием памяти, как и ожидалось. Я на 99% уверен, что следующий пример точен; это в значительной степени псевдокод, поэтому компиляция, очевидно, не будет.

Обновление:

Только что добавили 3-е решение благодаря nusi.

Неправильный путь (с памятью стека):

std::map<int, MyType1> myMap;

void firstFunctionRunFromThread1()
{
    MyType1 mt1;
    mt1.Test = "Test 1";
    myMap[0] = mt1;
}

void onlyFunctionRunFromThread2()
{
    MyType1 mt1 = myMap[0];

    // This actually does print "Test 1", so the memory is being accessed.
    std::cout << mt1.Test << endl;

    /* However, because we're using stack memory here, this value is lost
     * when we go back to thread #1. */
    mt1.Test = "Test 2";
}

void secondFunctionFromThread1()
{
    MyType1 mt1 = myMap[0];

    // This will actually print out "Test 1", where "Test 2" is expected!
    std::cout << mt1.Test << endl;
}

сложный , правильный метод (с использованием динамической памяти):

См. Также простой метод, который использует стековую память.

std::map<int, MyType1> myMap;

void firstFunctionRunFromThread1()
{
    // Use heap memory so the memory stays allocated.
    MyType1 *mt1 = new MyType1();
    mt1->Test = "Test 1";
    myMap[0] = *mt1;
}

void onlyFunctionRunFromThread2()
{
    /* Now, get a pointer to the object; we can't use stack memory
     * here because the values assigned would be lost as soon as 
     * we try and access them from secondFunctionFromThread1() */
    MyType1 *mt1 = &myMap[0];

    // As expected, this prints "Test 1"
    std::cout << mt1->Test << endl;

    /* Now, because we're assigning to memory on the heap, this will
     * stay assigned until the entire application exits, yay! */
    mt1->Test = "Test 2";
}

void secondFunctionFromThread1()
{
    /* Not sure if using heap memory here is neccecary, but we'll do
     * it anwyay just to play on the safe side... let me know if this
     * is pointless... */
    MyType1 *mt1 = &myMap[0];

    // Hurray, this prints "Test 2"!!! :)
    std::cout << mt1->Test << endl;
}

простой , правильный метод (использование стековой памяти правильно):

Спасибо Нуси за его ответ .

std::map<int, MyType1> myMap;

void firstFunctionRunFromThread1()
{
    MyType1 mt1;
    mt1.Test = "Test 1";
    myMap[0] = mt1;
}

void onlyFunctionRunFromThread2()
{
    /* Using the & before the variable turns it into a reference, so
     * instead of using stack memory, we use the original memory.
     * NOTE: Is this explanation correct? */
    MyType1 &mt1 = myMap[0];

    // This actually does print "Test 1", so the memory is being accessed.
    std::cout << mt1.Test << endl;

    // We're assigning to the reference, so this works.
    mt1.Test = "Test 2";
}

void secondFunctionFromThread1()
{
    MyType1 mt1 = myMap[0];

    // Prints "Test 1" as expected.
    std::cout << mt1.Test << endl;
}
0 голосов
/ 07 апреля 2009

Как уже было сказано, потоки ничего не делают с вашей памятью. У них нет личного пространства памяти. Они просто команды для планировщика вашей операционной системы реализовать «параллельное» выполнение 2 или более функций. Любые 2 функции, которые могут обращаться к одной и той же переменной, могут делайте это независимо от того, являются ли они резьбовыми (выполняются рядом) или нет (последовательно).

Часто нити выглядят как страшные монстры, но это действительно просто дать планировщику подсказку, чтобы добавить дополнительное выполнение указатель.

Чтобы отладить описанную проблему, просто измените более базовую тип переменной в двух функциях, например, int. Весьма вероятно, что ваш контейнер копирование данных, как только вы измените строку или что-то похоже.

    int i;    // some variable in the scope of both functions

void f1()
{
   while(true)
    {
       i++;
       i %= 128000;
    }
}
void f2()
{
     while(true)
            std::cout 
0 голосов
/ 07 апреля 2009

Используете ли вы блокировку при доступе к общему ресурсу (то есть строке)?

0 голосов
/ 07 апреля 2009

Когда поток заканчивается, его стек освобождается. Однако память кучи не зависит от потока, поэтому любые выделения в куче останутся там, пока вы не выполните очистку.

Распределена ли строка в стеке? Если это так, у вас нет общей строки между потоками, даже если один и тот же код выполняется двумя потоками.

В следующем примере функция foo выполняется двумя потоками:

void foo( void*)
{
   std::string myString;
   // Do something with your string....
}

myString не разделяется между потоками. Каждый поток имеет строковый объект в своем собственном стеке.

Итак, какова реальная ситуация, к которой вы обращаетесь?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...