It may depend on the event; for example, if you are consuming an event from a dll written in C# via a "field-like event", i.e.
public event EventHandler SomeEvent; // synchronized
(yes I know you asked about VB; I explain this below...)
then this is (per the C# language spec) automatically synchronized, so there are no multi-threading issues (and it helps here that delegates are immutable). However, even in the same library, a non-field-like event might not be synchronized - i.e.
private EventHandler myField;
public event EventHandler SomeEvent { // not synchronized
add { myField += value; }
remove { myField -= value; }
}
I know you asked about VB... but you are asking about VB as the comsumer. My point is that it depends on the publisher.
So if you can't control the publisher, then manually synchronizing sounds adviseable if you think that there is a threading risk for this scenario (in most cases, there is no need to synchronize; but if you know this is a threaded area, then synchronizing is sensible).
Also note that a common pattern when raising an event is to take a snapshot:
protected virtual void OnSomeEvent() {
EventHandler handler = SomeEvent; // assume the "field-like" version
if(handler!=null) handler(this, EventArgs.Empty);
}
My point here is that it is in theory possible for a subscriber to receive an event even after they think they have unsubscribed, so if extreme cases you might want to handle this manually, perhaps by keeping a flag somewhere (or just exception handling).
As requested, last code fragment in VB (via reflector) for comparison:
Protected Overridable Sub OnSomeEvent()
Dim handler As EventHandler = Me.SomeEvent
If (Not handler Is Nothing) Then
handler.Invoke(Me, EventArgs.Empty)
End If
End Sub