Notification Threads in the Profiling API
In most cases, the thread that generates an event also executes notifications. Such notifications (for example, FunctionEnter and FunctionLeave) do not need to supply the explicit ThreadID. Also, the profiler might decide to use thread-local storage to store and update its analysis blocks instead of indexing the analysis blocks in global storage, based on the ThreadID of the affected thread.
Note that these callbacks are not serialized. Users must protect their code by creating thread-safe data structures and by locking the profiler code where necessary to prevent parallel access from multiple threads. Therefore, in certain cases you can receive an unusual sequence of callbacks. For example, assume that a managed application is spawning two threads that are executing identical code. In this case, it is possible to receive a ICorProfilerCallback::JITCompilationStarted event for some function from one thread and a FunctionEnter callback from the other thread before receiving the ICorProfilerCallback::JITCompilationFinished callback. In this case, the user will receive a FunctionEnter callback for a function that may not have been fully just-in-time (JIT) compiled yet.