Re: Thread safety of events by Peter
Peter
Tue Jul 10 18:31:59 CDT 2007
On Tue, 10 Jul 2007 15:19:30 -0700, Armin Zingler <az.nospam@freenet.de>=
=
wrote:
> I have an object O1 that executes code in thread T1. O1 raises events.=
> There is an object O2 that runs in thread T2. O2 catches O1's events.
>
> Question:
> What happens if O2 is detaching the event handler at the same time as =
O1
> is raising the event? Do I have to handle this special situation =
> explicitly?
You sort of do.
First, keep in mind that when an event is raised in O1, even though O2 =
normally runs in T2, its event handlers are executed on T1, where O1 =
raised the event. You probably already knew that, but just want to =
clarify.
Now, that means you do have the potential of having O2 running on T2 =
unsubscribing at the same time that O1 running on T1 raises the event. =
=
There's a lot of event-based code out there that doesn't handle this =
situation, and usually it's okay because everything is actually running =
on =
the same thread. But when the potential for this condition exists, at a=
=
minimum you need to do something like this:
OnRaiseEvent(...)
{
EventType eventInstance =3D _eventField;
if (eventInstance !=3D null)
{
eventInstance(...);
}
}
This copies the event member field to the local variable eventInstance s=
o =
that if the member field is set to null between the time it's checked fo=
r =
null and the time it's actually used, that's not an issue. By copying i=
t, =
you ensure that what's checked for null is your local variable, not the =
=
member field that could change.
Now, there is the separate issue of O2 having unsubscribed while O1 now =
=
has a reference to it in its local variable. This means that even thoug=
h =
O2 has unsubscribed, it's not quite done yet. O1 still has a reference =
to =
O2, via the local variable, and O2 will still be called when the event =
variable is executed.
Whether this is a problem depends a lot on the design of O2 (and even of=
=
O1 to some extent). In particular, if O2 can still respond to an event,=
=
even though you have discarded it somewhere else, then there's really no=
=
problem. On the other hand, if you have a situation where O2 is =
disposable, and as part of disposing that's where you unsubscribe, and =
something else that is released as part of disposing is required in orde=
r =
to handle the event (get all that? :) ), then yes...you need to be able =
to =
sychronize access to the thing that is required, and in the event handle=
r =
make an explicit check to verify that your object is in fact still in a =
=
state in which it can handle the event. If it's not, then you would jus=
t =
ignore the event as it if never happened.
Once the O1 event raising method returns, the value in the local variabl=
e =
is released, and since the O1 instance member containing the event =
reference no longer refers to O2 either, you can at that point be assure=
d =
the event handler will not be called again. But you do need to be able =
to =
handle correctly the transient condition in which you think you've =
unsubscribed but could still get one more raised event.
Pete