Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / New Users / July 2007

Tip: Looking for answers? Try searching our database.

Thread safety of events

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Armin Zingler - 10 Jul 2007 23:19 GMT
Hi,

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?
Honestly I haven't read/heard that anybody ever cared about this. It would
be pretty much work to do because every event handling object would have to
prevent the event raiser from raising the event while detaching it's own
handlers. Not a good OO way also. I know the "instance members are not
thread safe" part of the Delegate/Multicastdelegate documentation, but I
also think that Events are a basic element in the runtime's infrastructure,
so maybe I don't have to handle the situation explicitly?

Armin
Peter Duniho - 11 Jul 2007 00:31 GMT
> 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.
[quoted text clipped - 3 lines]
> 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 = _eventField;

        if (eventInstance != null)
        {
            eventInstance(...);
        }
    }

This copies the event member field to the local variable eventInstance so  
that if the member field is set to null between the time it's checked for  
null and the time it's actually used, that's not an issue.  By copying it,  
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 though  
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 order  
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 handler  
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 just  
ignore the event as it if never happened.

Once the O1 event raising method returns, the value in the local variable  
is released, and since the O1 instance member containing the event  
reference no longer refers to O2 either, you can at that point be assured  
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
Peter Duniho - 11 Jul 2007 00:49 GMT
> [...]
> 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 order to handle the  
> event (get all that? :) ), then yes...you need to be able to sychronize  
> access to the thing that is required

To clarify:

The above is an example of where you'd need to synchronize access.  It's  
not meant to be the only case.  Obviously, putting the unsubscribe  
operation somewhere other than the Dispose() method doesn't necessarily  
fix things.  You could still manage to successfully unsubscribe, then call  
Dispose just after the event member field has been copied in the other  
thread, but all before the event handlers have been called, whether the  
operation to unsubscribe happens in the Dispose or not.

The key here is not where you unsubscribe, but whether disposing the  
object somehow invalidates some data structure that is required in order  
for the object to successfully handle the event.

Pete
Armin Zingler - 11 Jul 2007 01:17 GMT
> On Tue, 10 Jul 2007 16:31:59 -0700, Peter Duniho
> <NpOeStPeAdM@nnowslpianmk.com> wrote:
[quoted text clipped - 20 lines]
> object somehow invalidates some data structure that is required in
> order for the object to successfully handle the event.

I think I understand. :-) And yes, I know that the event handler is execute
in the same thread.

Though, what you described is not the main problem, sorry. :-) I can handle
the states and whatever. What I worried about was the fact, that the
invocation list is modified by removing one event handler (in T2) at the
same time as it is processed in order to raise the event (in T1). Raising
the event might lead to an exception due to the manipulation from the other
thread (T2).

In other words, do I have to do the following? (example only; shall only
show the concept)

  Class C1
     Public Event Progress()
     Public Locker As New Object
     Public Sub DoWork()
        '...
        SyncLock Locker
           RaiseEvent Progress()
        End SyncLock
        '...
     End Sub
  End Class

  Class C2
     Public Sub DoWork()
        Dim O1 As New C1
        addhandler O1.Progress,...
        '...
        SyncLock O1.Locker
           removehandler O1.Progress,...
        End SyncLock
     End Sub
  End Class

In this case, it's ensured that the event will not be raised (in T1) while
the handler is being removed (in T2).

Armin
Peter Duniho - 11 Jul 2007 02:04 GMT
> [...]
> Though, what you described is not the main problem, sorry. :-) I can  
[quoted text clipped - 3 lines]
> Raising the event might lead to an exception due to the manipulation  
> from the other thread (T2).

I know.  That is why in the code I posted, the invocation list is copied  
into a local variable before invoking the handlers.

> [...]
> In this case, it's ensured that the event will not be raised (in T1)  
> while the handler is being removed (in T2).

Yes, that is another way to do it.  I don't like exposing a  
synchronization object from one class to another class, but if you choose  
to do it that way, I believe it should work fine.  The big problem with  
that mechanism is that you wind up requiring every subscriber to the event  
to use the synchronization object.  Since events are supposed to be  
general purpose, this is an unusual design, and one that is likely to lead  
to maintenance issues in the future.

But if you are the only person who will ever maintain the code and the  
event is only to be used in this one very specific situation, it is a risk  
that you can probably afford.

You do need to synchronize access somehow though, however you do it.

Pete
John Saunders [MVP] - 11 Jul 2007 00:37 GMT
> Hi,
>
> 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.

O2 doesn't "catch" events at all. When O1 raises an event, if O2 has a
delegate in the handler list of the event, then the handler in O2 will be
called in thread T1.
Signature

John Saunders [MVP]

Armin Zingler - 11 Jul 2007 00:49 GMT
> "Armin Zingler" <az.nospam@freenet.de> wrote in message
> news:ed2Vhb0wHHA.3340@TK2MSFTNGP04.phx.gbl...
[quoted text clipped - 5 lines]
>
> O2 doesn't "catch" events at all.

No, it does. I wouldn't say it if it didn't. "Catch" is short for "contains
a procedure that is the event handler of an event of" - I thought, this is a
common term.

> When O1 raises an event, if O2 has
> a delegate in the handler list of the event, then the handler in O2
> will be called in thread T1.

Right.

Armin
Peter Duniho - 11 Jul 2007 01:54 GMT
>> O2 doesn't "catch" events at all.
>
> No, it does. I wouldn't say it if it didn't. "Catch" is short for  
> "contains a procedure that is the event handler of an event of" - I  
> thought, this is a common term.

In .NET, "catch" is typically used for exceptions, while "handles" is  
typically used for events.

While I might find less point in quibbling over the semantics than John,  
if we're going to argue about common usage, John's point is correct.  
"Catch" is not at all a common term used to describe event handling.

Pete
William Stacey [C# MVP] - 11 Jul 2007 03:26 GMT
I agree.  You throw and catch exceptions. We "invoke" (i.e. raise or call)
events.  catch is not normal usage for events.  The thread raising the event
executes/runs the handler (delegate) code  (unless you have some other
special need to invoke handler(s) delegates on other threads).

Signature

William Stacey [C# MVP]

| >> O2 doesn't "catch" events at all.
| >
[quoted text clipped - 10 lines]
|
| Pete
Armin Zingler - 11 Jul 2007 04:51 GMT
> I agree.  You throw and catch exceptions. We "invoke" (i.e. raise or
> call) events.  catch is not normal usage for events.  The thread
> raising the event executes/runs the handler (delegate) code  (unless
> you have some other special need to invoke handler(s) delegates on
> other threads).

Strictly speaking yes, but the context was clear, so...  Was just a
variation of "handle", "receive", "listen to"(, "catch"). Please note that
English is not my native language, therefore I thought it was clear in the
context. Really, "catch an event" is ambiguous? Ok, I'll remember. :-)

Armin
William Stacey [C# MVP] - 11 Jul 2007 22:31 GMT
|  Really, "catch an event" is ambiguous? Ok, I'll remember. :-)

IMO, yes.  Because your not catching anything, nor is there any context
switch happening - the calling thread is just running some delegates in a
list (top to bottom).
Armin Zingler - 11 Jul 2007 23:22 GMT
> |  Really, "catch an event" is ambiguous? Ok, I'll remember. :-)
>
> IMO, yes.  Because your not catching anything, nor is there any
> context switch happening - the calling thread is just running some
> delegates in a list (top to bottom).

In the German language, it is ok to use variations and different words for
the same thing as long as the context is clear and it is unambigiuous. What
is done here we would call splitting hairs. More important is to understand
each other and not to insist on the correct technical terms - as long as the
discussion is not about the correct terms. "Catch" was more metaphorically
spoken (catch it, handle it, grab it, get it, react on it or whatever). I
feel  sorry that I really didn't know that in the English language only the
one technical term "handle" is allowed. Sorry, but I'm restricted by my
school English (which is some years old). Please take this into
consideration.

I also thought that it is valid to say "fire" an event. Now that I know that
I always must use the correct technical term "raise" I will do this
invariably in future.

Armin
Armin Zingler - 11 Jul 2007 04:34 GMT
> On Tue, 10 Jul 2007 16:49:27 -0700, Armin Zingler
> <az.nospam@freenet.de> wrote:
[quoted text clipped - 12 lines]
> correct. "Catch" is not at all a common term used to describe event
> handling.

I wrote "catch an event", so what do you think I am referring to? An event
or an exception? Mind the context. Too unambigious to misunderstand.

Armin
Peter Duniho - 11 Jul 2007 05:32 GMT
> I wrote "catch an event", so what do you think I am referring to? An  
> event or an exception? Mind the context. Too unambigious to  
> misunderstand.

Sigh.

It's not a question of whether we understood what you meant.  It's a  
question of whether you wrote the correct thing.

People post all sorts of crazy things here, and those of us trying to help  
them are tasked with deciphering what they wrote.  Sometimes we can figure  
it out, sometimes we can't.  Sometimes we think we figured it out, but we  
didn't.

You using the word "catch" when what you really should have written was  
"handle" isn't the worst thing we've ever seen around here, by any stretch  
of the imagination.  I doubt anyone reading your post had any trouble  
understanding what you meant.  But that doesn't make it correct.

IMHO, it was a waste of bandwidth for John to comment on the misuse.  
However, he didn't actually post anything that was incorrect, and it's an  
even bigger waste of bandwidth for you to argue about whether "catch" is  
the right verb to use or not.  I doubt you can find a single page in MSDN  
that uses the word "catch" in connection with having the handler for an  
event called when that event is raised.

Pete
Armin Zingler - 11 Jul 2007 11:50 GMT
> On Tue, 10 Jul 2007 20:34:48 -0700, Armin Zingler
> <az.nospam@freenet.de> wrote:
[quoted text clipped - 6 lines]
>
> It's not a question of whether we understood what you meant.

No, it is. Period.

Armin
Jon Skeet [C# MVP] - 11 Jul 2007 19:42 GMT
> 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 should do, yes - at least if you foresee people subscribing to or
unsubscribing from the events from a different thread.

> Honestly I haven't read/heard that anybody ever cared about this.

See http://pobox.com/~skeet/csharp/threads/lockchoice.shtml

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Armin Zingler - 11 Jul 2007 19:54 GMT
> Armin Zingler <az.nospam@freenet.de> wrote:
> > I have an object O1 that executes code in thread T1. O1 raises
[quoted text clipped - 12 lines]
>
> See http://pobox.com/~skeet/csharp/threads/lockchoice.shtml

Ok, thanks. I'll have a look.

Armin

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.