The code sample “How to: Define Event Accessor Methods” at
http://msdn.microsoft.com/en-us/library/dw1dtw0d.aspx
appears to mutate the internal pE without taking locks. (It doesn’t look like Delegate::Combine does anything magical that would prevent issues.) It also does
void raise() {
if (pE != nullptr)
pE->Invoke();
}
which can be problematic if pE changes to null between the check and the Invoke(). I have two questions:
-
Am I right in that the existing code is not thread-safe?
-
Since I want a thread-safe version of the code, I was thinking of locking the
addandremovefunctions. Is it premature optimization to usevoid raise() { MyDel^ handler = pE; if (handler != nullptr) handler->Invoke(); }or should I just lock that function too?
All three accessors are thread-safe by default (
raiseincludes a null-check, and uses a local variable to avoid the race condition) unlike the example in the page you linked.When it comes to custom event implementations, you’re right about needing to synchronize the
addandremoveaccessors. Just put a mutex around the implementation. But there’s no need to throw away type safety by callingDelegate::Combineand then casting, since operator+and-are overloaded for delegate handles. Or you can go lockless, as follows:Define
removemutatis mutandis (new = pE - p;). And the code you gave forraisewill be perfectly fine for a custom event implementation.In summary, that MSDN sample is total garbage. And the simplest way to achieve thread-safety is with an auto-implemented event.