You have to add the only line to make the first snippet correct in multithreaded environment:
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
EventHandler myEvent = MyEvent;
Thread.MemoryBarrier();
if (myEvent != null)
{
myEvent(this, e);
}
}
Memory barrier refuses to reorder read and writes for both compiler and CPU. That's how volatile reads/writes are implemented. You can read more about memory barrier here.