.NET: How does the EventHandler race-condition fix work?

前端 未结 3 605
生来不讨喜
生来不讨喜 2021-02-08 04:24

There\'s the following pattern which is used to avoid a race condition when raising events in case another thread unsubscribes from MyEvent, making it null.

clas         


        
3条回答
  •  鱼传尺愫
    2021-02-08 05:12

    Update

    Here are some diagrams that should hopefully clear up confusion about copying references and assignment.

    First: copying a reference.

    x = y

    In the above diagram, the reference contained in y is copied into x. No one's saying the object is copied; mind you—they point to the same object.

    Second: assigning a new reference to a variable.

    y +=

    Forget about the += operator for a moment; what I want to highlight above is that y is being assigned a different reference, to a new object. This does not affect x because x is its own variable. Remember, only the reference (the "address" in the diagram) had been copied to y.

    Third: same thing, only to x.

    x +=

    The above diagrams depict string objects, only because those are easy to represent graphically. But it's the same thing with delegates (and remember, standard events are just wrappers around delegate fields). You can see how by copying the reference in y into x above, we have created a variable which will not be affected by subsequent assignments to y.

    That is the whole idea behind the standard EventHandler race condition "fix" we're all familiar with.


    Original Answer

    You are probably confused by this tricky little syntax:

    someObject.SomeEvent += SomeEventHandler;
    

    What's important to realize is that, as Ani points out in his answer, delegates are immutable reference types (think: just like string). Many developers mistakenly think they are mutable because the above code looks like I am "adding" a handler to some mutable list. This isn't so; the += operator is an assignment operator: it takes the return value of the + operator and assigns it to the variable on the left side.

    (Think: int is immutable, and yet I can do int x = 0; x += 1; right? It's the same thing.)


    EDIT: OK, technically this is not quite right. Here is what really happens. An event is actually a wrapper around a delegate field that is accessible only (to external code) by the += and -= operators, which are compiled to calls to add and remove, respectively. In this way it is very much like a property, which is (typically) a wrapper around a field, where accessing the property and calling = are compiled to calls to get and set.

    But the point still remains: when you write +=, the add method that gets called is internally assigning a reference to a new object to the internal delegate field. I apologize for oversimplifying this explanation in my initial answer; but the key principle to understand is the same.

    By the way, I am not covering custom events where you can put your own logic inside the add and remove methods. This answer only applies to the "normal" case.


    In other words, when you do this...

    EventHandler handler = SomeEvent;
    if (handler != null)
    {
        handler(this, EventArgs.Empty);
    }
    

    ...you are indeed copying a reference into a variable. Now that reference is in the local variable and won't itself be modified. If it was pointing to an actual object at the time of assignment, then it will continue to point at that same (immutable) object on the next line. If it was not pointing to an object (null), then it will still not point to an object on the next line.

    So if code elsewhere subscribed or unsubscribed to the event using +=, what it really did was change the original reference to point to a completely new object. The old delegate object is still around, and you've got a reference to it: in your local variable.

提交回复
热议问题