One Thread Updates a Structure Field, But Another Thread Does Not See the Update

  • Thread starter Thread starter eBob.com
  • Start date Start date
E

eBob.com

In my class which contains the code for my worker thread I have ...

Public MustInherit Class Base_Miner

#Region " Delegates for accessing main UI form "
Delegate Sub DelegAddProgressBar(ByVal uiform As Form1, ByRef si As
MTCC02.Form1.SiteRunOpts, _
ByVal numitems As Integer)
#End Region

Public globalinf As MTCC02.Form1.GlobalRunOpts
Public siteinf As MTCC02.Form1.SiteRunOpts
....

The constructor for this class does ...

Public Sub New(ByVal url As String, ByVal globalinfo As
MTCC02.Form1.GlobalRunOpts, _
ByRef siteinfo As MTCC02.Form1.SiteRunOpts)

....

globalinf = globalinfo 'save for LoadTextFile sub
siteinf = siteinfo 'save for LoadTextFile sub

It's the siteinf structure which I think is central to the problem I am
looking for some help with. What I think I am doing here is making the
structure available to all the methods of the Base_Miner class and all
classes which inherit from it. Right?

Still in the constuctor I do ...

Dim AddProgBar As DelegAddProgressBar = New DelegAddProgressBar(AddressOf
MTCC02.Form1.AddProgressBar)

globalinf.UIFormInst.Invoke(AddProgBar, New Object() {globalinf.UIFormInst,
siteinfo, NextDetailUrlsIndex})


Here in the constructor I think that I can use siteinf and siteinfo
interchangebly. Right?

On return from the Invoke the debugger shows that the "progbarProgress"
field of both siteinf and siteinfo is Nothing. ?!?!?

In my Form1 class, and this code is runs on my main/UI thread, I have ...

Public Shared Sub AddProgressBar(ByVal uiform As Form1, ByRef si As
SiteRunOpts, ByVal numitems As Integer)
si.progbarProgress = New ProgressBar
With si.progbarProgress
.Width = numitems \ 2
.Maximum = numitems
.Location = New Point(progbarProgress_xloc, si.yloc)
.Step = 1
End With
uiform.Controls.Add(si.progbarProgress)
End Sub 'AddProgressBar

At the end of this Sub, when I use the debugger and check the si argument,
the "progbarProgress" field is not Nothing and looks reasonable (to me!,
which is maybe not saying much). Also,the progressbar does get created - I
see it appear on the UI form. But back in the worker thread which is
running the Base_Miner code and which issued the Invoke which caused the
AddProgressBar code to be run the SiteRunOpts structure has Nothing in the
progbarProgress field.

What's going on here? Did the worker thread get its own copy of the
SiteRunOpts structure it was passed? How do I fix this?

Incidentally, does it matter is a structure is passed ByRef or ByVal so long
as you are only accessing fields within the structure? I think I've been
told it does not but earlier when I was passing the SiteRunOpts argument
ByVal I was wondering about that.

This has got me most frustrated, so I will be most grateful for any help.
Bob
 
In my class which contains the code for my worker thread I have ...

Public MustInherit Class Base_Miner

#Region " Delegates for accessing main UI form "
Delegate Sub DelegAddProgressBar(ByVal uiform As Form1, ByRef si As
MTCC02.Form1.SiteRunOpts, _
ByVal numitems As Integer)
#End Region

Public globalinf As MTCC02.Form1.GlobalRunOpts
Public siteinf As MTCC02.Form1.SiteRunOpts
...

The constructor for this class does ...

Public Sub New(ByVal url As String, ByVal globalinfo As
MTCC02.Form1.GlobalRunOpts, _
ByRef siteinfo As MTCC02.Form1.SiteRunOpts)

...

globalinf = globalinfo 'save for LoadTextFile sub
siteinf = siteinfo 'save for LoadTextFile sub

It's the siteinf structure which I think is central to the problem I am
looking for some help with. What I think I am doing here is making the
structure available to all the methods of the Base_Miner class and all
classes which inherit from it. Right?

Still in the constuctor I do ...

Dim AddProgBar As DelegAddProgressBar = New DelegAddProgressBar(AddressOf
MTCC02.Form1.AddProgressBar)

globalinf.UIFormInst.Invoke(AddProgBar, New Object() {globalinf.UIFormInst,
siteinfo, NextDetailUrlsIndex})

Here in the constructor I think that I can use siteinf and siteinfo
interchangebly. Right?

On return from the Invoke the debugger shows that the "progbarProgress"
field of both siteinf and siteinfo is Nothing. ?!?!?

In my Form1 class, and this code is runs on my main/UI thread, I have ...

Public Shared Sub AddProgressBar(ByVal uiform As Form1, ByRef si As
SiteRunOpts, ByVal numitems As Integer)
si.progbarProgress = New ProgressBar
With si.progbarProgress
.Width = numitems \ 2
.Maximum = numitems
.Location = New Point(progbarProgress_xloc, si.yloc)
.Step = 1
End With
uiform.Controls.Add(si.progbarProgress)
End Sub 'AddProgressBar

At the end of this Sub, when I use the debugger and check the si argument,
the "progbarProgress" field is not Nothing and looks reasonable (to me!,
which is maybe not saying much). Also,the progressbar does get created - I
see it appear on the UI form. But back in the worker thread which is
running the Base_Miner code and which issued the Invoke which caused the
AddProgressBar code to be run the SiteRunOpts structure has Nothing in the
progbarProgress field.

What's going on here? Did the worker thread get its own copy of the
SiteRunOpts structure it was passed? How do I fix this?

Incidentally, does it matter is a structure is passed ByRef or ByVal so long
as you are only accessing fields within the structure? I think I've been
told it does not but earlier when I was passing the SiteRunOpts argument
ByVal I was wondering about that.

This has got me most frustrated, so I will be most grateful for any help.
Bob

Sorry - I'm having a hard time following your post. As I understand
it, you are modifiying a structure on one thread and not seeing the
change on the other? Are you sure that they both are modifying the
same copy? Your question about ByVal vs. ByRef indicates that you have
a incomplete understanding of reference types vs. value types...
Which can definately lead to these kind of issues.

So, to quickly answer the question - yes, it most definately matters.

The long answer is why it matters.... A structure in VB.NET is an
object that inherits from system.valuetype. A class is an object that
inherits form system.reference type. What does that mean? It means
that like an Integer, a Byte, or any of the other numeric types, a
structures memory is allocated on the stack, not the heap. When a
structre is passed between methods, a byval call will cause a new copy
of the structure to be created. What that means is that any changes
made in the called procedure will not be propegated to the caller.
When a byref call is made, the address of the structure is passed - so
any changes made by the called procedure are propegated to the caller
because they are working on the same instance. This is with a value
type...

For instance:

Public Sub Main ()
Dim i As Integer = 10

Console.WriteLine ("i Before Call ByVal = {0}", i)
CallByValue (i)
Console.WriteLine ("i After Call ByVal = {0}", i)

Console.WriteLine ("i Before Call ByRef = {0}", i)
CallByRef (i)
Console.WriteLine ("i After Call ByRef = {0}", i)

End Sub

Private Sub CallByValue (ByVal i As Integer)
i = 100
End Sub

Private Sub CallByRef (ByRef i As Integer)
i = 100
End Sub

-- Output --
i Before Call ByVal = 10
i After Call ByVal = 10
i Before Call ByRef = 10
i After Call ByRef = 100

Where it becomes confusing somewhat is that this has the appearance of
being inconsitant with what happens with classes (reference types).
Because when they are passed byval, you can change properties on a the
object, and the changes are propegated to the caller... But, if you
realize that what is really being passed byval with a reference type
is actually the address of the object this will make sense. Reference
types are allocated dynamically on the heap, so what is actually
stored in an object variable is the memory location of the object.
What that means is that though you can change the state of the object
- you can not change the object reference...

That means in the called sub, if the object is passed by value, that
if you do:

theObjectParam = new MyObject()

The caller will not see the new object when the call returns. They
will still have their old refernce unchanged. Now, if they had passed
the object byref, their refrence would now point to the new
instance :)

Anyway, you might want to post a more complete example of your
problem (try to keep at small as possible). It would make it much
easier to spot any issues.
 
Incidentally, does it matter is a structure is passed ByRef or ByVal
so long as you are only accessing fields within the structure? I
think I've been told it does not but earlier when I was passing the
SiteRunOpts argument ByVal I was wondering about that.

Yes it does - structures are value types which means if you pass ByVal, the
structure will be duplicated and you'll have 2 copies of the data.

Regular objects are reference type objects - in which case passing
ByRef/ByVal is a moot point.
 
Regular objects are reference type objects - in which case passing
ByRef/ByVal is a moot point.

Not to argue... But, it's not exactly a moot point. It really depends
on what the method does. Now, normally I wouldn't do this - but, if
your method is intended to initialize the object reference then you
need to pass it ByRef.

Still, in most scenarios, then it really makes no difference.
 
Tom Shelton said:
Sorry - I'm having a hard time following your post. As I understand
it, you are modifiying a structure on one thread and not seeing the
change on the other? Are you sure that they both are modifying the
same copy? Your question about ByVal vs. ByRef indicates that you have
a incomplete understanding of reference types vs. value types...
Which can definately lead to these kind of issues.

So, to quickly answer the question - yes, it most definately matters.

The long answer is why it matters.... A structure in VB.NET is an
object that inherits from system.valuetype. A class is an object that
inherits form system.reference type. What does that mean? It means
that like an Integer, a Byte, or any of the other numeric types, a
structures memory is allocated on the stack, not the heap. When a
structre is passed between methods, a byval call will cause a new copy
of the structure to be created. What that means is that any changes
made in the called procedure will not be propegated to the caller.
When a byref call is made, the address of the structure is passed - so
any changes made by the called procedure are propegated to the caller
because they are working on the same instance. This is with a value
type...

For instance:

Public Sub Main ()
Dim i As Integer = 10

Console.WriteLine ("i Before Call ByVal = {0}", i)
CallByValue (i)
Console.WriteLine ("i After Call ByVal = {0}", i)

Console.WriteLine ("i Before Call ByRef = {0}", i)
CallByRef (i)
Console.WriteLine ("i After Call ByRef = {0}", i)

End Sub

Private Sub CallByValue (ByVal i As Integer)
i = 100
End Sub

Private Sub CallByRef (ByRef i As Integer)
i = 100
End Sub

-- Output --
i Before Call ByVal = 10
i After Call ByVal = 10
i Before Call ByRef = 10
i After Call ByRef = 100

Where it becomes confusing somewhat is that this has the appearance of
being inconsitant with what happens with classes (reference types).
Because when they are passed byval, you can change properties on a the
object, and the changes are propegated to the caller... But, if you
realize that what is really being passed byval with a reference type
is actually the address of the object this will make sense. Reference
types are allocated dynamically on the heap, so what is actually
stored in an object variable is the memory location of the object.
What that means is that though you can change the state of the object
- you can not change the object reference...

That means in the called sub, if the object is passed by value, that
if you do:

theObjectParam = new MyObject()

The caller will not see the new object when the call returns. They
will still have their old refernce unchanged. Now, if they had passed
the object byref, their refrence would now point to the new
instance :)

Anyway, you might want to post a more complete example of your
problem (try to keep at small as possible). It would make it much
easier to spot any issues.

Hi Tom, Thanks so much for your prompt response. I had indeed forgotten
that structures are value types. But I don't think that (should have!) had
anything to do with my problem. Long story short ... the multi-threading
was a red herring. I changed to a single thread and the problem persisted.
Then I changed the structure to a class and bingo! that fixed the problem.
Then I went back to multi-threading and all was still well. What continues
to confuse me is why fields in a structure passed ByRef should behave any
different from fields in a class passed ByRef. That appears to be what was
happening. (The caller sees a change to a field when it is in a class but
not when it is in a structure, both passed ByRef.) I will try to come up
with a complete but really simple example of what I was experiencing and
post it. Thanks again, Bob
 
Back
Top