I don't think the example solves the issue of race condition if we are raising multiple events right after each other with different arguments. In my case, I am consuming items from a producer-consumer queue and raising the same event for every consumed item, but with different arguments.
foreach (WorkItem workItem in _taskQ.GetConsumingEnumerable()) {
ExternalApplication.addedElementRetrievalEventHandler.doc = workItem.doc;
ExternalApplication.addedElementRetrievalEventHandler.elementId = workItem.elementId;
ExternalApplication.addedElementRetrievalEventHandler.pointCloudElements = workItem.pointCloudElements;
ExternalApplication.addedElementRetrievalEvent.Raise();
// Wait for the event to complete before queueing the next one.
// Otherwise the handler may have wrong data.
// Event uses data in the handler.
do {
Thread.Sleep(1000);
} while (ExternalApplication.addedElementRetrievalEvent.IsPending);
}
To ensure that the raised event gets the correct variables, which are not overridden by the next Raise() event, I wait till the raised event is no longer pending.
The problem is that IsPending seems to return false as soon as (or just before) the raised event starts the Execute() method. Here is an example of the Execute() method:
public void Execute(UIApplication ui_app) {
Thread.Sleep(60000);
// Use elementId here.
ElementId b = elementId;
}
I have added the sleep here to reproduce the issue. Let's consider the following execution order:
1) Event #1 raised with ElementId = 123
2) Event #1 is pending
3) Event #1 is no pending
4) Event #1 starts execution
5) Event #1 sleeps for 60 seconds.
6) Event #2 is raised with ElementId = 456
7) Event #2 is pending
8) Event #1 sleep is over and copies elementId to variable "b".
In the above case, b is 456 instead of 123.
I don't see how the method using lock mentioned above will avoid this problem. If I am missing something or if you have a workaround, please let me know.