5

我在 Linux 上使用 pthread 库。

我在线程 A 中分配了一个字符串,然后尝试在线程 B 中打印该字符串。但是,该字符串仅打印为空(我已验证它在线程 A 中有效)。

注意:字符串驻留在一个对象内,我怀疑它可能会被清理或重新实例化为空......容器对象不会给我一个段错误或任何东西,只是所有的值都是空的。

这是因为线程无法从其他线程访问内存,还是因为一旦线程 A 停止,内存就会被取消分配?或者两者都不是;这很可能是我的代码中的一个错误,但我只是想排除这种情况......

更新:

原来是内存问题。感谢您的回答,我自己也回答了这个问题,如果您同意/不同意,请对我的回答发表评论。

4

8 回答 8

9

与进程不同,线程在进程内部共享一个公共内存空间(每个线程都有自己的堆栈,但堆通常是共享的)。因此,当您退出线程时,从共享堆分配的内存不会自动释放。但是,例如,如果您在堆栈上分配了一个字符串对象并通过一个简单的指针将其传递到某处,则析构函数将在线程退出时释放内存。

于 2009-04-07T07:58:18.303 回答
2

尽管每个线程都有其“自己的”内存(即 - 它自己的堆空间)......内存就是内存。启动一个线程不会对另一个线程的内存做任何事情。线程 B 如何获取线程 A 中字符串的指针?

您需要提供更多详细信息...您是否将字符串放在堆上?如果它不在堆上,它可能会在线程 A 死亡时消失......否则你有一个错误......请发布更多!

于 2009-04-07T07:59:09.203 回答
1

是的,内存保持分配状态。

在包含字符串的类的 dtor 中放置一个断点或一些日志,看看发生了什么。

希望这可以帮助

于 2009-04-07T07:59:55.023 回答
1

尽管您的“正确方法”有效,但对于您的原始问题来说,它的解决方法太多了。

以下是您原始问题所需的唯一更改。

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

   ...

以你的“正确方式”,现在是你负责释放你用 new 分配的内存。在您的原始示例中,它是“自动”完成的(可以这么说)。

而且这两种解决方案都缺乏对地图或地图项目的锁定,这是一个完全不同的问题。

于 2009-04-08T17:37:17.387 回答
0

当一个线程完成时,它的堆栈被释放。但是,堆内存不是线程特定的,因此堆上的任何分配都将保留在那里,除非您执行清理。

字符串是否在堆栈上分配?如果是这样,即使两个线程运行相同的代码,线程之间也没有共享字符串。

在以下示例中,函数 foo 由两个线程运行:

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

myString 不在线程之间共享。每个线程在他自己的堆栈上都有一个字符串对象。

那么,您所指的真实情况是什么?

于 2009-04-07T07:59:05.057 回答
0

当您访问共享资源(即字符串)时,您是否使用了锁?

于 2009-04-07T07:59:25.773 回答
0

正如已经说过的,线程对你的记忆没有任何作用。它们没有任何私有内存空间。它们只是操作系统调度程序的命令,用于实现 2 个或更多功能的“并行”执行。可以访问同一变量的任何 2 个函数都可以这样做,无论它们是否是线程化的(并排执行)或不(顺序)。

通常线程看起来像一些可怕的怪物,但它实际上只是给调度程序一个添加额外执行指针的提示。

要调试您描述的问题,只需更改两个函数中更基本的变量类型,例如 int。一旦您更改字符串或类似内容,您的容器很可能会立即对数据进行一些内部复制。

    诠释我; // 两个函数范围内的一些变量

无效 f1()
{
   而(真)
    {
       我++;
       我 %= 128000;
    }
}
无效 f2()
{
     而(真)
            标准::cout
于 2009-04-07T14:04:44.787 回答
0

事实证明,这个问题是由不正确的内存使用引起的,正如预期的那样。我 99% 确定以下示例是准确的;它几乎是伪代码,所以显然不会编译。

更新:

感谢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;
}

简单、正确的方法(正确使用堆栈内存):

感谢nusi的回答

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;
}
于 2009-04-08T16:51:26.187 回答