Click to See Complete Forum and Search --> : static thread


hkullana
July 6th, 2007, 03:35 AM
hi,

i have a class called myClass, and the class has a private data called "static UINT thread (LPVOID param)" and another member functions called startthread(), func1(), func2(). the code is below


startthread()
{
AfxBeginThread(thread,this);
}


UINT myClass::thread(LPVOID param)
{
myClass* mc = (myClass*)param;

mc->func1(); // no error

... = mc->func2(); // run-time error
}


the second one gives a run-time error. Func2() is a function that returns a string func1() is a void function. I think the problem is about that the thread is static but i cannot find a solution, so i need help..

thanks...

Krishnaa
July 6th, 2007, 04:48 AM
Can ypu post the error details? What is inside the func2() ?? Are you sure that the class object passed to thread remains in memory untill the code execution reaches that line in thread ??

Have you tried debugging it ?

hkullana
July 6th, 2007, 07:56 AM
here is myClass:

#include <string>
using namespace std;

class myClass
{
public:
myClass(string str)
:Str(str)
{}

string Str;

void startThread()
{
AfxBeginThread(thread,this);
}

void func1()
{
AfxMessageBox("bbb");
}

string func2()
{
return Str;
}

static UINT thread(LPVOID param);

};


UINT myClass::thread(LPVOID param)
{
myClass* mc = (myClass*)param;
AfxMessageBox("does not give error here");

mc->func1();
string s = mc->func2();

AfxMessageBox("gives error here");
return 0;
}



when the button is hit i call the class

myClass mc("aaa");

and the member function:

mc.startThread

and it gives and error before the second MessageBox of the thread function.

Interestingly when i delete the line:

string s = mc->func2();

it does not give an error.

JVene
July 6th, 2007, 10:53 AM
when the button is hit i call the class

myClass mc("aaa");

and the member function:

mc.startThread


It appears that this snippet creates mc on the stack. When the startThread function is called, mc is certainly in scope.

At that point, there will be some scheduling activity by the OS which may temporarily stall this 'main' thread, while the new thread is created and initiated. During that time, it may be possible that mc remains in scope.

However, it seems likely that soon thereafter, this main thread will continue, and I suspect soon returns, leaving the function responding to the button press. Your assumption is that the new thread continues to function.

It does, but at that point mc has fallen from scope and evaporated. The thread is now using an object that no longer exists, but has no way of recognizing that fact.

The fact that the first call succeeds is genuinely a matter of accident. On a different machine, with more cores or faster processors, or perhaps even just a different version of the OS with different 'tuning' for the task scheduler, the first call might even fail - and it could happen the opposite way, the second might succeed, but then if you continue to extend THAT function, eventually mc falls from scope and the bug re-appears.

You must take some step to guarantee that mc remains in scope until the entire thread's actions upon it are ended, then delete it explicitly.

An object family can be created that generically works in this way for all future situations where you want to invoke a thread in this fashion.

In my own work, I have a QueProcessor. This represents a thread of execution. Simply by instantiating one as a member of the window thus:

QueProcessor q1;

I have a thread, up and running, represented by the member q1.

Now, I want to call some function in an object that's temporary. For that I have a template representing such a function call:

q1.Submit<MyObject>( &MyObject::func );

There are ways to create that so it's a little simpler:

q1.Submit( &MyObject::func );

It's a template function inside QueProcessor. The idea is that QueProcessor has a queue container - a list of 'jobs' the queue may be asked to perform. When that queue container is empty, q1 is idle.

When Submit is called, q1 is signaled that is has something to do. Submit added an object to the container inside q1, based on some generic "Process" object. The derived version (for this type of call) is a template class that owns an instantiation of MyObject, upon which a call to func is based. MyObject is guaranteed to be in scope during the call, since q1 is managing that.

This means that for all time, when this situation presents itself, I need never concern myself about this bug. It will never happen, as I've packaged the solution, debugged and tested over years of use, into my own library.

You may elect to ignore that advice for this project, only to discover you have to solve the problem again the next time it comes up. Until it dawns on you that every time you "do something again" - you've defined the need for an object in your own library - you will repeat the solution, with new bugs each time. You may even find yourself fixing the same bug created each occasion. At some point you'll become quite expert at 'doing this' each time, and develop various permutations for unique situations.

In my own work, each of these permutations, which I discovered when the need arose, have extended the family of objects representing QueProcessor, and it's siblings/cousins/friends. At some point you begin to realize that packaging solutions into a personal library is also the act of preserving your solutions and the time spent developing those solutions, such that you're handing yourself leverage for the future.

Do that for a few years and your own power is increased, your code becomes more solid, and you find yourself working on more ambitious projects you previously never had the time to 'get to' - because you were always solving the same problems over and over again.

Sorry for the drifting lecture, but this one issue was the genesis of the practice of creating my own thread library some 17 years ago, and I've never regretted it - just seemed like a great opportunity to point out the concept as coincident with a most classic problem.