views:

609

answers:

4

Hi all,

I know this may sound strange, but yes, it's 2009 and I need to write small application using BCB5 :)

The problem is that DynamicArray throws OutOfBound exception when trying to expand it from within worker thread.

I have global variable, let's say

DynamicArray<double> X;

In main thread I check the length of array and I get 0, which is OK (length wasn't set)

Application->MessageBox(itoa(X.Length,str , 10), "", MB_OK);

Below is my worker thread class

class ArrayModifierThread : public TThread
{
private:
DynamicArray<double> __thread X;

protected:
void __fastcall Execute();

public:
__fastcall ArrayModifierThread(bool CreateSuspended);
 void setX(DynamicArray<double> &a);
};

So far so good. Next I create new thread:

ArrayModifierThread *t = new ArrayModifierThread(true);
t->setX(X); // Pass reference to my global DynamicArray
t->Resume();

Here the Execute() method gets executed:

void __fastcall ArrayModifierThread::Execute()
{
 X.Length = 10;
 X[5] = 45.5;
}

What I'd expect is that global array is expanded and 6th element gets value of 45.5.

But closer investigation from within main thread gives Length = 0 and ArrayOfBounds Exception:

 Application->MessageBox(itoa(__X.Length,str , 10), "", MB_OK);
 Application->MessageBox(itoa(__X[5],str , 10), "", MB_OK);

Could anybody tell me what I've missed?

+1  A: 

In your Execute method you're modifying the thread's X field, not the global X variable. Although the setX method receives its argument by reference, the member variable is not a reference. It stores a copy of the DynamicArray value, and changing the Length property ensures that it refers to a unique array.

The setX function receives a reference to the global variable, as you correctly observe in your "answer," but it doesn't keep a reference to it. It instead makes a copy of it when it assigns the object's X field.

Perhaps you intended to declare X as a reference as well:

private:
  DynamicArray<double>& X;

That could work. Your setX function wouldn't work anymore since you're not allowed to "re-seat" a reference after it's been initialized. Instead, you'd need to initialize it in the thread's constructor:

ArrayModifierThread(DynamicArray<double>& a): X(a) { ... }

You could also store a pointer to the array instead of a reference:

private:
  DynamicArray<double>* X;
public:
  void setX(DynamicArray<double>& a) {
    X = &a;
  }
protected:
  void Execute() {
    X->Length = 10;
    (*X)[5] = 45.5;
  }

Something else you need to be aware of is that your code is not thread-safe. (Neither is mine here.) You have multiple threads modifying the same shared resource (the array) without any protection, such as a critical section. That's beyond the scope of this question, though. Search Stack Overflow and the rest of the Web first, and then come back to ask a new question if you need help with that issue.

Rob Kennedy
A: 

Well, it's not quite clear to me. I thought because of void setX(DynamicArray &a) the worker thread gets a reference to a global variable.

Could you post sample code how should it be done?

This is how my void setX(DynamicArray &a) looks like:

void ArrayModifierThread::setX(DynamicArray<double> &a)
{
   X = a;
}
+1  A: 

It will not work as the member X is not a reference. When you call set() you are making a local copy of the object, then Execute() is modifying the local version.

class ArrayModifierThread : public TThread
{
    private:
        DynamicArray<double> __thread X;


void __fastcall ArrayModifierThread::Execute()
{
    X.Length = 10;
    X[5] = 45.5;
 }

There are three alternative solutions:

Make the your local member variable X a reference.
Since the reference must be initialized on construction you can not use set() to modify it, so pass the reference to the constructor of your object and save it there.

Make the your local member variable X a pointer. When using set() take the address of the passed in parameter (note it is a reference so you will get the address of the object it is referencing). This also means that the execute needs to be modified to take into account that X is a pointer.

Do not use a local member variable. Just modify the global directly.

Martin York
A: 

Thank you guys, it works!