multithreaded tcp/ip monitoring application

P

Peter Duniho

Punit said:
Thank you very much for the pointers to get started. Regarding
synchronization of shared objects, currently I have a user defined class
called NRMonitor and it has the following structure:

class NRMonitor
{
public connstatus;
}

What type is "connstatus"?
I have an array of objects for the above class, to store data for multiple
connections. And I also have an ArrayList so that I can store these objects
as Link List, because whenever the user removes the connection from the List,
I remove the corresponding object in the list.

Not that it likely matters, but do you store them as an ArrayList or a
LinkedList? The two are not the same.
So basically multiple threads with their own receive/connect functions would
be updating this datastructure. But since they will be accessing different
elements within the array to update their corresponding connstatus variable
value .. so do I still need synchronization in this case?

Well, yes and no. You don't need to synchronize access to the ArrayList
itself per se, since you are only changing the data structures referred
to by the ArrayList, not the ArrayList itself.

However, presumably at some point some code takes the information stored
in those data structures and relates that to the user information
somehow. At that point, access to the data structures themselves does
need to be synchronized. So, in that scenario yes...you need to
synchronize the access to the data structures themselves.
Or if you can suggest me a simpler mechanism of storing this data and
updating it from the threads?

IMHO, Control.BeginInvoke() is the simplest. Use it to execute a
delegate that will take the received data and actually do something
useful with it, such as storing the information into your "connstatus"
value (whatever that is). Use the form that contains your UI as the
instance on which to call BeginInvoke(). (Don't confuse
Control.BeginInvoke() with Delegate.BeginInvoke()...the former is what
you want, the latter will just create the same synchronization issues
you're trying to solve).
Also about the UI, I was thinking of adesign where, on the left I would have
a tree view of the IP addresses of the remote machines. On the right I have a
tab control which will show the parameters corresponding to the selected IP
address on the left.

I don't know why you are using a TreeView...it seems to me that a plain
ListBox or ListView would be fine for displaying a simple list of IP
addresses. That said, I don't see any reason you _can't_ use a TreeView
either.

As far as the tab control goes, that should be fine as well.
Presumably, you'll have code to update the currently displayed tab based
on changes to the currently selected IP address as those changes occur,
as well as update the newly displayed tab based on the current state of
the selected IP address as the user switches tabs.

If you use BeginInvoke() to execute the delegate that does the updating
of the "connstatus" data structure, then you can safely force an update
to the currently displayed tab at the same time, as well as safely read
the current "connstatus" data any time you switch tabs.
So unlike my current application where I am updating my UI from within the
Receive thread, here I am just planning to update the Array objects
pertaining to each connection and just display the selected connection
statistics. But I am really not sure how complicated the code would be to
implement this...

If you design it correctly, it's simple. If not, well... :)

Pete
 
G

Guest

I wanted to try using Tree view to learn how to work with it.. thats why I
used it :)

connstatus is of type bool.

I am storing my objects in an ArrayList. I did not know ArrayList was
different from LinkedList...
Since I would be updating my UI while changing the elements in the
arraylist, I understand that I have to use synchronization.

I shall try to implement what you suggested and get back if I have questions.

Thanks once again!
 
G

Guest

Hi,

Thanks for your constant help. I am now having trouble sometimes and im
guessing it has to do with mul-threading. As suggested by you, I used the
"lock" synchronization mechanism to manage a shared arraylist object.

I have a UI Thread that continuously takes the information from that shared
object and depending on the node(IP Address) selected, it chooses that info
from the shared arraylist object and it displays that on the UI. Its
accessing the shared arraylist within the "lock" enclosed block. Within the
the "lock" block I call a Control.Invoke(delegate) method that actually
updates the UI. So should I encode that also withing "lock" block?


At the same time when I try to click connect on that node, the connect event
also tries to access that shared arraylist to update its contents. I have
that code included within "lock".

Looks like everything is synchronized. But When I try to click "Connect"
while the UI thread is running, the application hangs sometimes. Sometimes it
doesnt. I am guessing there is some kind of deadlock that happens randomly.
Wantto make sure my code works right.

Here are the two methods that r trying to access the shared resource .

private void UIThread()
{
while (!AppShutDown)
{
lock (locker)
{
if (NRArrayList.Count > 0) //Checks if tree has any nodes
{
this.Invoke(this.m_DelegateUpdateUI); //calls the
UI update //function that does not have a lock
}
}
Thread.Sleep(500);
}
this.Invoke(new EventHandler(this.CloseMe));
}
private void DelegateUpdateUI()
{
if (!AppShutDown)
{
if (IPTreeView.SelectedNode.Index >= 0)
{
foreach (NetworkRecorder nr in NRArrayList)
{
if (IPTreeView.SelectedNode.Text ==
nr.recorderip.ToString())
{
if (nr.monitoring)
{
switch (nr.connstatus)
{
case true:
//update text on the form
lblstatus.text="running";
break;

case false:
lblConnStatus.Text = "Not Running";
break;
}
}
else
{
lblConnStatus.Text = "None";

}
}
}
}
}
}


The question is , do I need to enclose the code in the DelegateUpdateUI
using "lock"??

Now simultaneouslt the connect event when fired does the following:

private void mnuConnect_Click(object sender, EventArgs e)
{

lock (locker)
{
foreach (NetworkRecorder nr in NRArrayList)
{
if (nr.recorderip.ToString() ==
IPTreeView.SelectedNode.Text)
{
this.numThreads++;
nr.client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
nr.remoteEP = new IPEndPoint(nr.recorderip, 8001);
nr.monitoring = true;
nr.client.BeginConnect(nr.remoteEP, new
AsyncCallback(ConnectCallback), nr);
break;
}
}
}

This calls Connect CallBack function which is also accessing the shared
resource ultimately.

private void ConnectCallback(IAsyncResult ar)
{
NetworkRecorder nr = (NetworkRecorder)ar.AsyncState;
if ((nr.monitoring)&&!(AppShutDown))
{
try
{
nr.client.EndConnect(ar);
this.numThreads--;
nr.connstatus = true;
nr.state = new StateObject();
nr.state.workSocket = nr.client;
// Begin receiving the data from the remote device.
this.numThreads++;
nr.client.BeginReceive(nr.state.buffer, 0,
nr.state.buffer.Length, 0, new AsyncCallback(ReceiveCallback), nr);

}
catch (Exception)
{

nr.connstatus = false;
nr.client.Close();
nr.client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
Thread.Sleep(500);
nr.client.BeginConnect(nr.remoteEP, new
AsyncCallback(ConnectCallback), nr);

}
}
else
{
nr.client.Close();
if (AppShutDown)
this.Invoke(new EventHandler(this.CloseMe));
if (nr.monitoring)
this.numThreads--;
}
}

Now should ConnectCallBack also have "lock" since its also accessing the
shared resource.

Please give me an idea how to synchronize the shared arraylist object among
all these methods.

Thanks a lot!!
 
P

Peter Duniho

Punit said:
Hi,

Thanks for your constant help. I am now having trouble sometimes and im
guessing it has to do with mul-threading. As suggested by you, I used the
"lock" synchronization mechanism to manage a shared arraylist object.

After looking at the code you posted, I'm not sure you need the lock.
I have a UI Thread that continuously takes the information from that shared
object and depending on the node(IP Address) selected, it chooses that info
from the shared arraylist object and it displays that on the UI.

IMHO, this is the part of your design that could use the most help. In
particular, you've got this thread that's polling the data every half
second. However, your network i/o code already knows when changes
happen. So it would be better to have the network i/o code initiate the
UI update.

Doing this will simplify synchronization, and get rid of the extra
thread completely.
Its
accessing the shared arraylist within the "lock" enclosed block. Within the
the "lock" block I call a Control.Invoke(delegate) method that actually
updates the UI. So should I encode that also withing "lock" block?

My experience has been that using Control.Invoke() with some other
synchronization object is a recipe for deadlock. As you seem to have
found. :)
At the same time when I try to click connect on that node, the connect event
also tries to access that shared arraylist to update its contents. I have
that code included within "lock".

Looks like everything is synchronized. But When I try to click "Connect"
while the UI thread is running, the application hangs sometimes. Sometimes it
doesnt. I am guessing there is some kind of deadlock that happens randomly.

You guess correctly, I think. Invoke() will not return until the
Control instance's thread has had a chance to retrieve the invocation
and execute it. When you call Invoke(), you have already obtained the
lock for your synchronization object, which prevents the Control
instance's thread from getting it. If the Control instance's thread
attempts to get the locking object at this point, it will block. It
won't be able to do anything until it receives the lock, but it's not
going to get the lock because the other thread won't be able to do
anything until the Invoke() completes, which won't happen because the
Control instance's thread is blocked.

Deadlock.

However, as I mention above, I don't believe you need the extra thread
anyway.
[code snipped]
The question is , do I need to enclose the code in the DelegateUpdateUI
using "lock"??

Not exactly. That is, since you are using Control.Invoke(), that code
will be executed on the same thread that is normally using the ArrayList
(I presume...that appears to be your design). However, before you call
Invoke(), you do access the ArrayList.Count property. Also, since you
do something based on the value of this property, it would probably be
unwise to let go of the lock until you are done doing what depends on
the lock.

But the fact is, you don't really need that extra thread anyway, so the
locking issue (at least in that part of your code) just goes away. If
you really must poll, you should just set a Forms.Timer object to go off
every 500ms and do the UI update in the event raised by the Timer. This
Timer will execute on the main thread and so there won't be any
synchronization issues with respect to the ArrayList itself.

Even better would be to follow my suggestion and do away with the
polling altogether.
[more code snipped]
Now should ConnectCallBack also have "lock" since its also accessing the
shared resource.

Please give me an idea how to synchronize the shared arraylist object among
all these methods.

In addition to what I already wrote:

It appears to me that the only data that actually needs to be shared
between threads is the nr.connstatus value. However, since the network
i/o code already knows when this value changes, there doesn't appear to
be a real need for the non-network i/o code to actually access that
value. It would be better for the network i/o code to simply use
Invoke() (or even BeginInvoke()) to update the UI in response to changes
in the value.

You don't even need for the invoked delegate to access the
NetworkRecorder class directly at all. Rather than create a situation
where you need to synchronize access to an instance of NetworkRecorder
(which would be required if you use BeginInvoke(), which is otherwise a
better way to go), just have the network i/o report a change by passing
to the delegate a string representing the IP address, and the true/false
value of the connstatus value. That's all the UI really needs to know,
and by limiting the data passed to those copies of the actual data in
the NetworkRecorder, you avoid the synchronization issue altogether.

One last thing: I notice you keep a counter that looks like it's
supposed to track active threads. However, you should be aware that the
BeginXXX() methods for the Socket class do not normally actually create
a new thread. Your counter is really counting outstanding i/o
requests...no threads are active until an i/o completes, and you could
have a single thread wind up processing a sequence of simultaneously
created i/o requests. There's not actually a 1-to-1 mapping between i/o
requests and threads.

Whether this is actually important for your application or not, I don't
know. It looks like it might not be, but I figured I'd mention it anyway.

Pete
 
G

Guest

It appears to me that the only data that actually needs to be shared
between threads is the nr.connstatus value. However, since the network
i/o code already knows when this value changes, there doesn't appear to
be a real need for the non-network i/o code to actually access that
value. It would be better for the network i/o code to simply use
Invoke() (or even BeginInvoke()) to update the UI in response to changes
in the value.

You don't even need for the invoked delegate to access the
NetworkRecorder class directly at all. Rather than create a situation
where you need to synchronize access to an instance of NetworkRecorder
(which would be required if you use BeginInvoke(), which is otherwise a
better way to go), just have the network i/o report a change by passing
to the delegate a string representing the IP address, and the true/false
value of the connstatus value. That's all the UI really needs to know,
and by limiting the data passed to those copies of the actual data in
the NetworkRecorder, you avoid the synchronization issue altogether.

Your idea sounds really good. I wouldnt need to worry about synchronization
issues...

Here is what I udnerstand, please let me know if its correct.

I need to update the UI with various values and not only conn status.
However, I get the idea that I could pass all the info of the entire object
off as an arguement( copying those values to a local object of
NetworkRecorder class) to the updateUI delegate from the network i/o code.
and in the updateUI delegate function i check if that particular ip address
is selected in the tree view or not. Based on that I choose to update the UI
or not.

While I try this, Please let me know if this implementation seems to be fine.


One last thing: I notice you keep a counter that looks like it's
supposed to track active threads. However, you should be aware that the
BeginXXX() methods for the Socket class do not normally actually create
a new thread. Your counter is really counting outstanding i/o
requests...no threads are active until an i/o completes, and you could
have a single thread wind up processing a sequence of simultaneously
created i/o requests. There's not actually a 1-to-1 mapping between i/o
requests and threads.


I can understand what you are saying regarding the thread counter, but in my
case, irrespective of the connection being established or not, I am calling
the connectCallBack function in a loop until the connection successfully
establishes because I need to continuously monitor the remote application.
Remember I had asked you bout this and based on your suggeston, then I had
decided to enclose the endconnect function in the try block and if the
connection failed, the catch block would close the client, update the UI with
the status and try the whole process again. Thats why whether the connection
takes place or not , I will definitely have a thread running which is
continuously trying to connect.

After the connection gets establishes, I decrement the number of threads
counter because logically the connection thread ends and receive thread
starts. And increment the thread counter when it starts receiving. And the
same logic here.

I had to keep track of number of threads because at the end I am
implementing a function which does a graceful shutdown of all the
threads(where it needs to know the thread count) before it closes the
application.

Please let me know if you find any flaws in this design or if you have any
better suggestions on implementing this.


Thanks very much. I am learning a lot from your suggestions!
 
P

Peter Duniho

Punit said:
[...]
Here is what I udnerstand, please let me know if its correct.

I need to update the UI with various values and not only conn status.
However, I get the idea that I could pass all the info of the entire object
off as an arguement( copying those values to a local object of
NetworkRecorder class) to the updateUI delegate from the network i/o code.
and in the updateUI delegate function i check if that particular ip address
is selected in the tree view or not. Based on that I choose to update the UI
or not.

While I try this, Please let me know if this implementation seems to be fine.

As far as I understand it, it seems mostly fine. I'm not sure that
making a whole new copy of your NetworkRecorder instance is correct.
Don't you have references to other objects in there? Like your Socket
instance?

I think it might make more sense to create a status class that contains
the specific data relevant to the update. Then your NetworkRecorder
class can contain an instance of that status class, and when it needs to
invoke the delegate for updating the UI, it can clone that (you can
implement ICloning easily by just calling MemberwiseClone() in the
Clone() method of your new class.

Alternatively, you could just pass all of the values for the status as
parameters for the invoke, by putting them in an object[] passed to the
Invoke() method.

The key is to not pass references that may be in use by the thread, like
the Socket instance. You can say now "well, I just won't touch those
parts", but it's much better to just design the code so it's not
possible to in the first place.
I can understand what you are saying regarding the thread counter,

I don't think you do.
but in my
case, irrespective of the connection being established or not, I am calling
the connectCallBack function in a loop until the connection successfully
establishes [...] Thats why whether the connection
takes place or not , I will definitely have a thread running which is
continuously trying to connect.

No, you do not have a thread running. That's my point. Calling
BeginConnect() does not start a new thread. It creates an i/o request.
If and when it completes, the i/o request is dequeued by an
already-existing thread. That same thread could handle any number of
other i/o requests as well.
After the connection gets establishes, I decrement the number of threads
counter because logically the connection thread ends and receive thread
starts. And increment the thread counter when it starts receiving. And the
same logic here.

And you have the same error in your logic here too. Calling
BeginReceive() does not start a new thread. It only creates an i/o
request. As with the BeginConnect() case, that i/o request will
eventually be dequeued and processed by an already-existing thread.
I had to keep track of number of threads because at the end I am
implementing a function which does a graceful shutdown of all the
threads(where it needs to know the thread count) before it closes the
application.

Well, you're not tracking the number of threads. You're tracking the
number of i/o requests. Which may be fine, and desirable for your
design, but you should understand that you aren't tracking threads.

Pete
 
G

Guest

As far as I understand it, it seems mostly fine. I'm not sure that
making a whole new copy of your NetworkRecorder instance is correct.
Don't you have references to other objects in there? Like your Socket
instance?

I think it might make more sense to create a status class that contains
the specific data relevant to the update. Then your NetworkRecorder
class can contain an instance of that status class, and when it needs to
invoke the delegate for updating the UI, it can clone that (you can
implement ICloning easily by just calling MemberwiseClone() in the
Clone() method of your new class.

Alternatively, you could just pass all of the values for the status as
parameters for the invoke, by putting them in an object[] passed to the
Invoke() method.


The key is to not pass references that may be in use by the thread, like
the Socket instance. You can say now "well, I just won't touch those
parts", but it's much better to just design the code so it's not
possible to in the first place.

If I clone an object ,for example the Status Class object that contains
string types (which are reference types) will I be able to perform a deep
copy of those objects? After implementing memberwiseclone, will i not be
referring to the same objects as before or will this do a deep copy of the
object. If it does a shallow copy then will it not have synchronization
issues when i am accessing the object but at the same time updating it?

Well, you're not tracking the number of threads. You're tracking the
number of i/o requests. Which may be fine, and desirable for your
design, but you should understand that you aren't tracking threads.

Now I understand what you are trying to say. But to do a clean shutdown of
all the pending threads,sockets how should i keep track of them and how
should i ensure all of them end properly before the application shuts down.
 
P

Peter Duniho

Punit said:
If I clone an object ,for example the Status Class object that contains
string types (which are reference types) will I be able to perform a deep
copy of those objects?

I should clarify: it's not reference types per se that are a problem.
It's mutable reference types, that might be changed by one thread but
accessed by another, or are specifically not thread-safe.

As far as performing a deep copy goes, that depends on the type. But,
for instance, I would not expect a deep copy that includes a Socket
instance to be useful or practical. IMHO, it is better to create a data
structure that contains just the data you need, in a way that has copied
the data so that no other thread will be modifying.

Note too my previous point that you don't really need a separate data
structure at all. You _can_ just pass the values needed for the UI
update as individual parameters to the method if you like. As long as
the number of values is relatively small, this would be my preferred
approach.

If passing the values as parameters, the same issues apply with respect
to reference types, but at least you don't need to worry about creating
a whole new class, or of implementing ICloneable for that class (neither
are difficult things, but if you don't have to and there's not a
significant benefit from doing so, why bother?).
After implementing memberwiseclone, will i not be
referring to the same objects as before or will this do a deep copy of the
object. If it does a shallow copy then will it not have synchronization
issues when i am accessing the object but at the same time updating it?

Yes, a memberwise-clone is referring to the same objects. If they are
strings, this isn't a problem. That was my point. Sorry that I didn't
make that clear enough; I admit my previous post was a little vague on that.

The point here is that the data structure won't contain things that
_will_ be a problem if they are simply cloned by MemberwiseClone. They
should either be value types, or if they are reference types they should
be reference types that are thread-safe and not mutable by other threads.

The main example of something that would _not_ be in this data structure
is the Socket reference; it shouldn't be needed for the UI update, and
is exactly the sort of thing that can mutate after the fact, causing
potential confusion between threads should they happen to try to use the
object at the same time.
Now I understand what you are trying to say. But to do a clean shutdown of
all the pending threads,sockets how should i keep track of them and how
should i ensure all of them end properly before the application shuts down.

I don't think that the counting mechanism you're using is necessarily
wrong. If it's doing what you want, then that's fine. My only point
was with respect to the mistaken impression you appeared to have as to
what it was you were counting. For the moment, the question of whether
you are counting threads or counting i/o requests may not matter. But
if you come back later to the code, it might and at that point having
the correct knowledge of what the counter is actually counting could be
important.

So, the main change I'd suggest is to rename the counter to that it more
correctly reflects what it's counting. If the implementation otherwise
works for you, then you shouldn't have to change anything else.

Pete
 
G

Guest

:

I should clarify: it's not reference types per se that are a problem.
It's mutable reference types, that might be changed by one thread but
accessed by another, or are specifically not thread-safe.

As far as performing a deep copy goes, that depends on the type. But,
for instance, I would not expect a deep copy that includes a Socket
instance to be useful or practical. IMHO, it is better to create a data
structure that contains just the data you need, in a way that has copied
the data so that no other thread will be modifying.
Yes, a memberwise-clone is referring to the same objects. If they are
strings, this isn't a problem. That was my point. Sorry that I didn't
make that clear enough; I admit my previous post was a little vague on that.

The point here is that the data structure won't contain things that
_will_ be a problem if they are simply cloned by MemberwiseClone. They
should either be value types, or if they are reference types they should
be reference types that are thread-safe and not mutable by other threads.

The main example of something that would _not_ be in this data structure
is the Socket reference; it shouldn't be needed for the UI update, and
is exactly the sort of thing that can mutate after the fact, causing
potential confusion between threads should they happen to try to use the
object at the same time.

I was under the impression that we need to synchronize access to all shared
variables between the threads because the examples i saw that generally books
giv for synchronization is generally a integer counter that is shared between
different threads. If all we have to worry about is mutable reference
variables and not value types, then why should someone worry about a integer
counter being accessed by multiple threads?

Or did I still get the concept wrong??!! I am so confused... Why a string
value or an integer value does not need synchronized access by multiple
threads?

For example if one thread say mythread is making changes to the string
variable say str which is immutable, its basically creating another string
with the modified value and re-assigning the new reference to str. now when
we call invoke and pass str as a parameter.... are we not passing the same
reference. So basically the delegate would have the reference to the same
string which was modified in mythread. So if the delegate concurrently
accesses the str value while mythread is acessing it, why wouldnt it be a
problem? Similar thing for the integer counter...?
 
P

Peter Duniho

Punit said:
I was under the impression that we need to synchronize access to all shared
variables between the threads because the examples i saw that generally books
giv for synchronization is generally a integer counter that is shared between
different threads. If all we have to worry about is mutable reference
variables and not value types, then why should someone worry about a integer
counter being accessed by multiple threads?

I am sorry if I'm not explaining this well. Let me try again...

The issue here is whether the variable is a) mutable, and b) that
mutation can be seen by multiple threads. Value types can be mutable as
well, but what I'm saying is that if you pass a copy of the value type,
then the original can mutate and it doesn't matter. Passing a copy of a
reference type variable just passes the reference, and so mutations in
the instance can still be seen by multiple threads.
Or did I still get the concept wrong??!! I am so confused... Why a string
value or an integer value does not need synchronized access by multiple
threads?

The string type is a reference type, but a string instance is immutable
and the string class is thread-safe. So, you can for all intents and
purposes treat it in the same way you'd treat a copy of a value type.
That is, you can pass the value of a string variable without any problem
to another thread, because even though it's a reference type, you are
guaranteed that the instance's properties won't actually change.

The same is not true of the string variable itself. That is, if you
have a string variable and it itself is being accessed by multiple
threads, then access to the _variable_ needs to be synchronized. But in
that case, the variable itself is mutable and that's why you need the
synchronization. Likewise a mutable value type variable (such as the
integer counter you see in the examples you're talking about); as long
as you pass the _value_ of that variable, you're fine. It's when the
variable itself can be accessed by multiple threads that the issue comes
up, because in that case the threads are all operating on the same value.
For example if one thread say mythread is making changes to the string
variable say str which is immutable, its basically creating another string
with the modified value and re-assigning the new reference to str.

So far, so good. You are correct: changes to the string require a new
instance of a string object, which is then assigned back to the original
variable "str".
now when
we call invoke and pass str as a parameter.... are we not passing the same
reference.

You are passing the value of the reference variable. But, and this is
very important, the "str" variable itself is not referenced by the
invoked delegate. Only the value it contains is.
So basically the delegate would have the reference to the same
string which was modified in mythread.

The delegate has a reference to the same string instance, yes. But not
the variable referencing the string.
So if the delegate concurrently
accesses the str value while mythread is acessing it, why wouldnt it be a
problem? Similar thing for the integer counter...?

Define "access". If by "access" you simply mean that the reference is
read, then the answer is that the string class is thread-safe, and so
using it from multiple threads is fine.

If by "access" you mean that the reference is written to, then the
answer is that the string class is immutable, and so this kind of access
cannot happen.

Now, if you change your example to be a mutable class, then yes...that's
potentially a problem. This is why I recommend not passing reference
values for mutable types to invoked delegates if it's not necessary.

Note that if you pass the value of an integer counter to an invoked
delegate, you are not passing the counter itself, but rather the value
contained in the counter at the moment you invoked the delegate. The
counter itself can change without affecting the delegate's observed
value, because the delegate has a copy of the value, not the original
counter.

In the examples I presume you are looking at, the counter itself is
accessed by multiple threads, and so synchronization needs to be done.

Maybe some examples will help (I hope...I especially hope they don't
confuse things more :) )):

// inherit Control to make it clear that we can call BeginInvoke.
// Also, note that I'm using BeginInvoke, so that I can illustrate
// the thread issues with just two threads. The same issues could
// exist using Invoke, but only if some thread other than the one
// calling Invoke is modifying the values. This could in fact be
// the main thread on which the invoked delegates run, but it could be
// some other thread as well. It just depends on the design of the
// program.
class TwoThreaded : Control
{
// The "volatile" keyword ensures that both threads, should they
// access these variables at the same time, get the correct, current
// value. This doesn't address synchronization issues; it just
// ensures that if you have a synchronization bug, at least the
// values are correct given the written code and the order the
// statements execute.
volatile int _i;
volatile string _str;

// Assume the instance uses this function in a second thread
void Thread1()
{
_i = 5;

// A copy of _i is passed, the value of 5
BeginInvoke(Delegate1, new object[] { _i });

// Even if the above invocation isn't run yet, it
// still gets 5 when it does, even though we change
// the variable here
_i = 10;

_str = "string 1";

// The reference contained by _str is passed; this is
// a copy of the reference, but not a copy of the object
// itself. It is also not a reference to the variable _str.
BeginInvoke(Delegate2, new object[] { _str });

// As with the above change to _i, this change to _str does
// not affect the reference passed to Delegate2. It sets
// a new reference for _str, but the string object "string 1"
// is still referenced by the parameter passed to Delegate2.
_str = "string 2";

// Here no parameters are passed. Delegate3 has an explicit
// use of the instance member _i.
BeginInvoke(Delegate3);

// If this statement executes before Delegate3, then the
// delegate will see this new value of _i:
_i = 15;

// And just to make sure there's at least some potential
// for making things more confusing, here's an anonymous
// delegate:

BeginInvoke((MethodInvoker)delegate() { Text = _str; });

_str = "string 3";
// This is very much like Delegate3. The anonymous delegate
// contains the instance member _str, and so if _str changes
// after the call to Invoke but before the delegate actually
// executes, the more-recent value for _str will be seen by
// the delegate.

// Finally, an example that is potentially very confusing, but
// is important to understand if you are using anonymous
// delegates:

string strLocal = "string 4";

BeginInvoke((MethodInvoker)delegate() { Text = strLocal; });

strLocal = "string5";

// This is just like the example where _str is passed. With
// the anonymous delegate, if the variable is contained in the
// delegate and can also be referenced elsewhere, that's a
// synchronization issue.
}

void Delegate1(int i)
{
// the value of i is the copy passed in Invoke.
// This is a copy of _i, not _i itself, and so _i
// may change without affecting the value of i
}

void Delegate2(string str)
{
// the value of str is the reference passed in Invoke
// Likewise, this is a copy of _str's reference (not
// a copy of the object itself, just of the reference
// to the object). The value of _str can be changed,
// setting it to a new object instance, without
// affecting the value of str.
}

void Delegate3()
{
int i = _i;

// the value of i is whatever _i is at the time this
// code runs. This easily could be different from
// the value of _i at the time Invoke() was called

// Note that other than the synchronization issue, this is
// otherwise fine. That is, if you really do want to see
// what the most up-to-date value for _i is, it's okay to
// do that.
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top