views:

619

answers:

9

I'm using the pthread library on Linux.

I'm assigning a string in thread A, and then trying to print the string in thread B. However, the string just prints out empty (I have verified that it works in thread A).

Note: The string resides inside an object, which I suspect may be getting cleaned up or re-instantiated empty... The container object doesn't give me a seg fault or anything, just all the values are empty.

Is this because threads cannot access memory from other threads, or because the memory is being unallocated once thread A stops? Or is it neither; it could well be a bug in my code, but I just wanted to rule this out...

Update:

Turns out it was a memory issue. With thanks to your answers, I have also answered this my self, please do comment on my answer if you agree/disagree.

A: 

How is B getting access to the string in A? Threads share the same global memory, so if you have something like:

#include <iostream>
#include <string>

// global
std::string theString = "I am a string.";

void theThread(void*)
{
    std::cout << theString << std::endl; // should work
}

int main(void)
{


    std::cout << theString << std::endl; //works, right?

    /* create thread */
    /* wait for thread to finish */

    return 0;
}

But if you have something like:

#include <iostream>
#include <string>

void theThread(void* data)
{
    std::string theString = *(reinterpret_cast<std::string*>(data));

    std::cout << theString << std::endl; // see notes
}

int main(void)
{
    // local
    std::string theString = "I am a string.";    

    std::cout << theString << std::endl; //works, right?

    /* create thread, passing &theString to the thread */

    // Printing here should work!

    /* wait for thread to finish */

    return 0;
}

But if you begin leaving main as the thread begins getting the string, the thread might get a bogus string. The problem might be you not waiting for the thread.

Sorry I can't be more specific with the actual functions themselves, my thread experience is limited in POSIX, good in Windows, and...boost::thread rocks.

Hopefully something in there gives you an idea what might be wrong.

GMan
I have nothing further to add to this except that if you are familiar with boost threads, then the pthreads API will look like a second cousin
1800 INFORMATION
Sweet. I just never got around to using it before I found boost, I should probably check it out sometime.
GMan
+5  A: 

Threads, unlike processes, share a common memory space inside a process (each thread has its own stack, but heaps are typically shared). Therefore, when you quit a thread, allocated memory is not freed automatically. But, for example, if you have allocated a string object on the stack and passed it somewhere by a simple pointer, the destructor will free the memory when the thread exits.

MaxVT
I'm pretty sure this was my problem... I've posted an answer to this question with what I think was causing the issue! Thanks.
nbolton
A: 

When a thread is finished, its stack is deallocated. However, the heap memory is not thread specific, so any allocations on heap will remain there, unless you perform cleanup.

Is the string allocated on stack? If so, you don't have shared string between the threads, even if the same code is run by the two threads.

In the following example, the function foo is run by two threads:

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

myString is not shared between the threads. Each thread has a string object on his own stack.

So, what is the real situation you are refering to?

Cătălin Pitiș
+2  A: 

Although each thread has its "own" piece of memory (ie - it's own heap space)... memory is memory. Kicking off a thread doesn't do anything to another thread's memory. How does Thread B get the pointer to the string in Thread A?

You need to give more details... Are you putting the string on the heap? If it is not on the heap, it will probably go away when Thread A dies... otherwise you have a bug... Please post more!

Tom
Sorry the details were sketchy... I felt the app was too complex to post an example (as that could very well be wrong), anyway, I've posted an answer to this question with the solution! Thanks.
nbolton
A: 

Do you use a lock when you access the shared resource (i.e. the string) ?

Indeera
+1  A: 

Yes, the memory stays allocated.

Put a break point or some logging into the dtor of the class containing the string, and see what's happening.

Hope this helps

Binary Worrier
A: 

As has been already said, threads do nothing to your memory. They do not have any private memory space. They are just commands to your operating system's scheduler to implement 'parallel' execution of 2 or more functions. Any 2 functions that can access the same variable, can do so, regardless if they are threaded(executed side by side) or not(sequentially).

Often threading looks like some fearsome monster, but it is really just giving the scheduler a hint to add an extra execution pointer.

To debug the problem you describe, just change a more basic type of variable in the two functions, like an int. It is very likely that your container does some internal copying of data as soon as you change the string or something similar.

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

void f1()
{
   while(true)
    {
       i++;
       i %= 128000;
    }
}
void f2()
{
     while(true)
            std::cout 
AndreasT
A: 

Turns out, the issue was caused by incorrect use of memory, as expected. I'm 99% sure the following example is accurate; it's pretty much pseudo code, so wont compile, obviously.

Update:

Just added a 3rd solution thanks to nusi.

The wrong way (with stack memory):

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

The complicated, correct method (using heap memory):

See also the simple method which uses stack memory.

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

The simple, correct method (using stack memory correctly):

Thanks to nusi for his answer.

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;
}
nbolton
Thanks for the tip, and thanks for the answer.
nbolton
+1  A: 

Although your "correct way" works, it's way too much fix for your original problem.

Below is the only change which you needed for your original problem.

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

   ...

In your "correct way", it's you who is now responsible to free up the memory which you allocated with new. In your original example it was done 'automatically' (so to say).

And both solutions lack locking of the map or the items of your map, which is an entirely different question to ask.

Yeah you're right, this way does work. Thanks.
nbolton
nbolton
Reference is the correct term for that syntax.