READ EVENTS actually should be called PROCESS EVENTS, read sounds so passive, what matters in this state is that OS things can happen, so not just form events, Windows events in the sense of any events and also not only of system processes, of any Process.
DOEVENTS in short is like saying "go into READ EVENTS (or as I suggest PROCESS EVNETS) status" for a short time, continue when no events are queued. DOEVENTS FORCE in comparison will wait for an event to happen if none is currently queued.
DOEVENTS usually should be done right before and after a lengthy running query, where I consider before more important as getting idle after a long running query lets events happen anyway.
If some event happens right before a lengthy query starts and was not yet processes, its processing will have to wait in the event queue until the query finishes, also with Autoyield=.t., so that's a bad luck case. DOEVENTS right before is like "let's get done with simple fast tasks, before we go into this next lengthy query". A query is just one statement. That's also perhaps one reason some prefer xBase code to a query, though the right queries can overall run faster, also just because they keep the focus on them running instead of events being processed, i.e. treat an ideally still very short period of being unresponsive with getting the result faster.
Also with Autoyield=.T. you don't get into a long running query, unless you would burden the query with something that could go to the PROCESS EVENTS state. But SQL only allows calling functions within expressions, for example as a where clause to return a boolean value or as a term for a value that becomes a computed field in the field list. But an additional clause like Isnull(_vfp.Docmd('Doevents')) turns the command to a function call. Unfortunately that would unreasonably extend the query time as you give the system a lot more chances to process events and even if no events are pending this will have a base wait time, a bit like Sleep(0) will just dispense of the rest of its time slice the OS grants a process/thread before it makes a CPU core switch to another.
When you do a lot of processing in a loop one after another without pause, not only SQL, forms and controls don't have the chance to show refreshed. As VFP is the Win-forms domain of Windows UI we're bound to the Windows OS notification system (aka event queue and events) letting Windows Forms (which VFP forms are on a basis level) work with message notifications triggering things related to Windows like their drawing.
The underlying nature of what causes this effect of VFP is that it's mainly single threaded, that means it's easily becoming unresponsive, as there is very low parallelism. VFP processes are only using one core, unless you do multiprocessing as something like ParallelFox. Also because Windows measures whether a process is responding and adds "(Not Responsive)" to the title bar, when it sends a certain test message and the process doesn't respond after a timeout. And VFP is like that. Which doesn't mean that process crashed, just it's busy and currently spikes the use of at least one CPU core, which poses a risk to the system stability, but not as long as that causes problems like deadlocks on some other resources than the CPU.
It can't cause the whole system to become unresponsive, as there still is the OS main loop of process switching to give all currently running processes a 'fair' (by whatever terms) time slice and stop them when that slice has past, no matter what the process currently does, even within a single SQL statement (which internally in C++ is lots of code lines running anyway, but even this won't matter as that works on the level of the CPU state and CPU instructions, the CPUs "instruction pointer", registers and more. All that is backed up and restored and so swapped with a previous backup. For the swapped-in process it's like nothing happend in between, which actually means it can happen in between single CPU instructions.
But with single core CPUs Foxpro was very sticky and resistant to allow its single thread to be interrupted. I'm stepping on half knowledge land here, I'm not a CPU expert, I just understand the basics of how an OS works and shares it's resources like the CPU cores. I'm not sure, for example, if multiple threads already also can make use of multiple cores. I think so, which means Calvin Hsias multithreading solution could make a VFP application more responsive, if you devote one thread to allow events to happen, i.e. have a thread that only does "READ EVENTS" all the time. I never tested this idea, though. I fear it's not that simple to stay responsive or this could be a standard for any process to have an idle thread in it.
To explain the least, a thread is a subprocess, if you will, it can run real parallel to other threads while the same process has its time slice of currently being "the process" of one core. And there I think is the knowledge gap to understand whether the threads are bound to the same core or can actually run in parallel for real on separate cores.
I think there would be some kind of load balancing mechanism of an OS, perhaps based on optimizing the Idle Process (literally called that in the task manager) and try to offer as many CPU time to any other than this process whenever that's possible, because time spent in that process is really meaning the whole system is waiting for anything to happen next.
Chriss