MyObject o = my_objects[i];
//now a second thread has set my_objects to null, and/or invoked my_objects.Remove(o)
o.myMethod();
That's fine. The variable my_objects holds references to objects, not the objects themselves. When you make a new variable o, it holds a second reference to the same object. Then you clear my_objects, which means you can't refer to the actual object through my_objects anymore. But that doesn't change the fact that you still have o, and you can still refer to the actual object through o. (And that in turn implies that the object itself still exists. C# is a garbage-collected language, which, to a first approximation means that objects are guaranteed to stick around until you're done with them. Non-managed C++ is different in this respect, and Objective-C is different in yet a third way. Not that you asked.)
Effectively, in this respect, references to MyObject behave just like primitive types like int. If I have an array of int, and I do
int oi = my_ints[i];
then I can do whatever I want with my_ints (clear it, remove items from it) and that won't change the fact that oi still exists.
The thing about reference types like MyObject, and the reason you got confused enough to ask this question, is that with MyObject both variables (o and my_objects[i]) still refer to the same actual object, so if you mutate the object via my_objects[i].gainSpiderPowers(), you'll see the effects of that mutation no matter whether you're looking via my_objects[i] or via o.